Skip to content

Commit

Permalink
fix #150: serial can now be an 20 octets string, number or hex
Browse files Browse the repository at this point in the history
Supports now: 20 octets
Strings like: "stringserial"
Hex like: "0x1234567890abcdef", "1234567890abdef"
Numbers like: 1234567890

[ci skip]
  • Loading branch information
Dexus committed Oct 8, 2017
1 parent 6da3254 commit 960f077
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 247 deletions.
39 changes: 39 additions & 0 deletions lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,45 @@ var tempDir = process.env.PEMJS_TMPDIR || osTmpdir()
* @module helper
*/

/**
* helper function to check is the string a number or not
* @param {String} str String that should be checked to be a number
*/
module.exports.isNumber = function (str) {
if (Array.isArray(str)) {
return false
}
/*
var bstr = str && str.toString()
str = str + ''
return bstr - parseFloat(bstr) + 1 >= 0 &&
!/^\s+|\s+$/g.test(str) && /^\d+$/g.test(str) &&
!isNaN(str) && !isNaN(parseFloat(str))
*/
return /^\d+$/g.test(str)
}

/**
* helper function to check is the string a hexaceximal value
* @param {String} hex String that should be checked to be a hexaceximal
*/
module.exports.isHex = function isHex (hex) {
return /^(0x){0,1}([0-9A-F]{1,40}|[0-9A-F]{1,40})$/gi.test(hex)
}

/**
* helper function to convert a string to a hexaceximal value
* @param {String} str String that should be converted to a hexaceximal
*/
module.exports.toHex = function toHex (str) {
var hex = ''
for (var i = 0; i < str.length; i++) {
hex += '' + str.charCodeAt(i).toString(16)
}
return hex
}

// cipherPassword returns an array of supported ciphers.
/**
* list of supported ciphers
Expand Down
2 changes: 1 addition & 1 deletion lib/openssl.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function spawn (params, binary, callback) {
if (--needed < 1) {
finished = true
if (code) {
if (code === 2 && stderr === '') {
if (code === 2 && (stderr === '' || /depth lookup: unable to/.test(stderr))) {
return callback(null, code, stdout, stderr)
}
return callback(new Error('Invalid openssl exit code: ' + code + '\n% openssl ' + params.join(' ') + '\n' + stderr), code)
Expand Down
37 changes: 26 additions & 11 deletions lib/pem.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ function createCSR (options, callback) {
* @param {String} [options.serviceKey] Private key for signing the certificate, if not defined a new one is generated
* @param {String} [options.serviceKeyPassword] Password of the service key
* @param {Boolean} [options.selfSigned] If set to true and serviceKey is not defined, use clientKey for signing
* @param {String|Number} [options.serial] Set a serial max. 20 octets - only together with options.serviceCertificate
* @param {String} [options.hash] Hash function to use (either md5 sha1 or sha256, defaults to sha256)
* @param {String} [options.csr] CSR for the certificate, if not defined a new one is generated
* @param {Number} [options.days] Certificate expire time in days
Expand Down Expand Up @@ -335,14 +336,25 @@ function createCertificate (options, callback) {
params.push('--TMPFILE--')
if (options.serial) {
params.push('-set_serial')
// set the serial to the max lenth of 20 octets ()
// A certificate serial number is not decimal conforming. That is the
// bytes in a serial number do not necessarily map to a printable ASCII
// character.
// eg: 0x00 is a valid serial number and can not be represented in a
// human readable format (atleast one that can be directly mapped to
// the ACSII table).
params.push('0x' + ('0000000000000000000000000000000000000000' + options.serial.toString(16)).slice(-40))
if (helper.isNumber(options.serial)) {
// set the serial to the max lenth of 20 octets ()
// A certificate serial number is not decimal conforming. That is the
// bytes in a serial number do not necessarily map to a printable ASCII
// character.
// eg: 0x00 is a valid serial number and can not be represented in a
// human readable format (atleast one that can be directly mapped to
// the ACSII table).
params.push('0x' + ('0000000000000000000000000000000000000000' + options.serial.toString(16)).slice(-40))
} else {
if (helper.isHex(options.serial)) {
if (options.serial.startsWith('0x')) {
options.serial = options.serial.substring(2, options.serial.length)
}
params.push('0x' + ('0000000000000000000000000000000000000000' + options.serial).slice(-40))
} else {
params.push('0x' + ('0000000000000000000000000000000000000000' + helper.toHex(options.serial)).slice(-40))
}
}
} else {
params.push('-CAcreateserial')
}
Expand Down Expand Up @@ -855,18 +867,21 @@ function checkPkcs12 (bufferOrPath, passphrase, callback) {
/**
* Verifies the signing chain of the passed certificate
* @static
* @param {String} PEM encoded certificate
* @param {Array} List of CA certificates
* @param {String|Array} PEM encoded certificate include intermediate certificates
* @param {String|Array} List of CA certificates
* @param {Function} callback Callback function with an error object and a boolean valid
*/
function verifySigningChain (certificate, ca, callback) {
if (!Array.isArray(certificate)) {
certificate = [certificate]
}
if (!Array.isArray(ca)) {
ca = [ca]
}

var files = [
ca.join('\n'),
certificate
certificate.join('\n')
]

var params = ['verify',
Expand Down
30 changes: 15 additions & 15 deletions test/pem.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,28 @@ function checkError (error, expectError) {
function checkEcparam (data, min, max) {
expect(data).to.be.an('object').that.has.property('ecparam')
expect(data.ecparam).to.be.a('string')
expect(/^\n*-----BEGIN EC PARAMETERS-----\n/.test(data.ecparam)).to.be.true()
expect(/\n-----END EC PARAMETERS-----\n/.test(data.ecparam)).to.be.true()
expect(/\n-----BEGIN EC PRIVATE KEY-----\n/.test(data.ecparam)).to.be.true()
expect(/\n-----END EC PRIVATE KEY-----\n*$/.test(data.ecparam)).to.be.true()
expect(/^\r?\n*-----BEGIN EC PARAMETERS-----\r?\n/.test(data.ecparam)).to.be.true()
expect(/\r?\n-----END EC PARAMETERS-----\r?\n/.test(data.ecparam)).to.be.true()
expect(/\r?\n-----BEGIN EC PRIVATE KEY-----\r?\n/.test(data.ecparam)).to.be.true()
expect(/\r?\n-----END EC PRIVATE KEY-----\r?\n*$/.test(data.ecparam)).to.be.true()
var matchup = /-----BEGIN EC PRIVATE KEY-----[\s\S]+-----END EC PRIVATE KEY-----/.exec(data.ecparam)
expect(matchup[0].trim().length).to.be.within(min + 1, max - 1)
}

function checkDhparam (data, min, max) {
expect(data).to.be.an('object').that.has.property('dhparam')
expect(data.dhparam).to.be.a('string')
expect(/^\n*-----BEGIN DH PARAMETERS-----\n/.test(data.dhparam)).to.be.true()
expect(/\n-----END DH PARAMETERS-----\n*$/.test(data.dhparam)).to.be.true()
expect(/^\r?\n*-----BEGIN DH PARAMETERS-----\r?\n/.test(data.dhparam)).to.be.true()
expect(/\r?\n-----END DH PARAMETERS-----\r?\n*$/.test(data.dhparam)).to.be.true()
expect(data.dhparam.trim().length).to.be.within(min + 1, max - 1)
}

function checkPrivateKey (data, min, max, encrypted) {
expect(data).to.be.an('object').that.has.property('key')
expect(data.key).to.be.a('string')
if (encrypted) { expect(/ENCRYPTED\n/.test(data.key)).to.be.true() }
expect(/^\n*-----BEGIN RSA PRIVATE KEY-----\n/.test(data.key)).to.be.true()
expect(/\n-----END RSA PRIVATE KEY-----\n*$/.test(data.key)).to.be.true()
if (encrypted) { expect(/ENCRYPTED\r?\n/.test(data.key)).to.be.true() }
expect(/^\r?\n*-----BEGIN RSA PRIVATE KEY-----\r?\n/.test(data.key)).to.be.true()
expect(/\r?\n-----END RSA PRIVATE KEY-----\r?\n*$/.test(data.key)).to.be.true()
expect(data.key.trim().length).to.be.within(min + 1, max - 1)
}

Expand All @@ -62,8 +62,8 @@ function checkCSR (data, expectClientKey) {
expect(data[k]).to.be.a('string')
})
if (expectClientKey) { expect(data.clientKey).to.equal(expectClientKey) }
expect(/^\n*-----BEGIN CERTIFICATE REQUEST-----\n/.test(data.csr)).to.be.true()
expect(/\n-----END CERTIFICATE REQUEST-----\n*$/.test(data.csr)).to.be.true()
expect(/^\r?\n*-----BEGIN CERTIFICATE REQUEST-----\r?\n/.test(data.csr)).to.be.true()
expect(/\r?\n-----END CERTIFICATE REQUEST-----\r?\n*$/.test(data.csr)).to.be.true()
}

function checkCertificate (data, selfsigned) {
Expand All @@ -72,8 +72,8 @@ function checkCertificate (data, selfsigned) {
expect(data).to.have.property(k)
expect(data[k]).to.be.a('string')
})
expect(/^\n*-----BEGIN CERTIFICATE-----\n/.test(data.certificate)).to.be.true()
expect(/\n-----END CERTIFICATE-----\n*$/.test(data.certificate)).to.be.true()
expect(/^\r?\n*-----BEGIN CERTIFICATE-----\r?\n/.test(data.certificate)).to.be.true()
expect(/\r?\n-----END CERTIFICATE-----\r?\n*$/.test(data.certificate)).to.be.true()
if (selfsigned) { expect(data.clientKey).to.equal(data.serviceKey) } else { expect(data.clientKey).to.not.equal(data.serviceKey) }
}

Expand All @@ -84,8 +84,8 @@ function checkCertificateData (data, info) {
function checkPublicKey (data) {
expect(data).to.be.an('object').that.has.property('publicKey')
expect(data.publicKey).to.be.a('string')
expect(/^\n*-----BEGIN PUBLIC KEY-----\n/.test(data.publicKey)).to.be.true()
expect(/\n-----END PUBLIC KEY-----\n*$/.test(data.publicKey)).to.be.true()
expect(/^\r?\n*-----BEGIN PUBLIC KEY-----\r?\n/.test(data.publicKey)).to.be.true()
expect(/\r?\n-----END PUBLIC KEY-----\r?\n*$/.test(data.publicKey)).to.be.true()
}

function checkFingerprint (data) {
Expand Down
Loading

0 comments on commit 960f077

Please sign in to comment.