diff --git a/lib/formats/pem.js b/lib/formats/pem.js index 859cfa1..b43e472 100644 --- a/lib/formats/pem.js +++ b/lib/formats/pem.js @@ -1,4 +1,4 @@ -// Copyright 2015 Joyent, Inc. +// Copyright 2018 Joyent, Inc. module.exports = { read: read, @@ -32,14 +32,22 @@ function read(buf, options, forceType) { buf = buf.toString('ascii'); } - var lines = buf.trim().split('\n'); + var lines = buf.trim().split(/[\r\n]+/g); - var m = lines[0].match(/*JSSTYLED*/ - /[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/); + var m; + var si = -1; + while (!m && si < lines.length) { + m = lines[++si].match(/*JSSTYLED*/ + /[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/); + } assert.ok(m, 'invalid PEM header'); - var m2 = lines[lines.length - 1].match(/*JSSTYLED*/ - /[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/); + var m2; + var ei = lines.length; + while (!m2 && ei > 0) { + m2 = lines[--ei].match(/*JSSTYLED*/ + /[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/); + } assert.ok(m2, 'invalid PEM footer'); /* Begin and end banners must match key type */ @@ -53,6 +61,8 @@ function read(buf, options, forceType) { alg = m[1].trim(); } + lines = lines.slice(si, ei + 1); + var headers = {}; while (true) { lines = lines.slice(1); diff --git a/lib/formats/x509-pem.js b/lib/formats/x509-pem.js index 56d78eb..3155ef0 100644 --- a/lib/formats/x509-pem.js +++ b/lib/formats/x509-pem.js @@ -29,14 +29,24 @@ function read(buf, options) { var lines = buf.trim().split(/[\r\n]+/g); - var m = lines[0].match(/*JSSTYLED*/ - /[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/); + var m; + var si = -1; + while (!m && si < lines.length) { + m = lines[++si].match(/*JSSTYLED*/ + /[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/); + } assert.ok(m, 'invalid PEM header'); - var m2 = lines[lines.length - 1].match(/*JSSTYLED*/ - /[-]+[ ]*END CERTIFICATE[ ]*[-]+/); + var m2; + var ei = lines.length; + while (!m2 && ei > 0) { + m2 = lines[--ei].match(/*JSSTYLED*/ + /[-]+[ ]*END CERTIFICATE[ ]*[-]+/); + } assert.ok(m2, 'invalid PEM footer'); + lines = lines.slice(si, ei + 1); + var headers = {}; while (true) { lines = lines.slice(1); diff --git a/test/assets/jim-x509-text.pem b/test/assets/jim-x509-text.pem new file mode 100644 index 0000000..e5f3cb3 --- /dev/null +++ b/test/assets/jim-x509-text.pem @@ -0,0 +1,46 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + ab:fd:52:3c:3e:52:c0:7b + Signature Algorithm: sha1WithRSAEncryption + Issuer: DC = jim, DC = com + Validity + Not Before: Jul 22 01:04:26 2016 GMT + Not After : Jul 22 01:04:26 2017 GMT + Subject: DC = jim, DC = com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (1024 bit) + Modulus: + 00:d5:9a:5d:d8:0d:5a:a2:98:5c:95:80:af:2b:80: + 41:22:b4:0f:be:1a:ae:bb:ad:7a:af:29:5e:34:57: + 91:a5:4f:b3:86:40:bf:a2:d3:3a:d7:f2:29:0b:47: + c2:29:0a:f5:5c:b3:7e:c5:ef:c2:a8:84:5f:70:0e: + 3f:39:e2:79:1c:50:26:cd:87:42:62:86:8a:f7:23: + 96:98:96:9b:c4:16:53:b8:4b:c9:e3:57:0f:4a:3a: + b6:3e:2f:df:f4:00:b4:40:2b:a5:0e:f7:a8:13:c1: + 13:96:e6:8f:fc:82:dc:83:c5:42:71:10:5d:83:89: + 6c:de:82:e9:90:25:6d:52:37 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 10:53:47:a4:a6:91:97:6b:42:06:7e:8f:25:e1:46:56:ae:a0: + 4e:84:38:db:ac:e8:76:2d:c4:81:53:4a:22:1c:1f:e9:58:70: + 25:88:3c:bc:86:a5:d0:03:7a:eb:d7:b8:ab:72:6e:39:f2:85: + 05:ba:91:07:bc:1d:ba:c9:08:8e:d6:e3:4b:41:c2:2e:5d:38: + 86:a1:40:36:c5:c7:a9:82:36:1b:65:a8:67:d5:66:d8:4c:9b: + 8a:16:d7:0d:c6:43:95:b2:af:83:9a:3d:f8:ca:f7:35:46:8a: + f8:95:ca:a5:83:97:e8:3c:4f:1a:1f:63:8d:ae:81:b6:3f:03: + 3b:79 +-----BEGIN CERTIFICATE----- +MIIByzCCATQCCQCr/VI8PlLAezANBgkqhkiG9w0BAQUFADAqMRMwEQYKCZImiZPy +LGQBGRYDamltMRMwEQYKCZImiZPyLGQBGRYDY29tMB4XDTE2MDcyMjAxMDQyNloX +DTE3MDcyMjAxMDQyNlowKjETMBEGCgmSJomT8ixkARkWA2ppbTETMBEGCgmSJomT +8ixkARkWA2NvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1Zpd2A1aophc +lYCvK4BBIrQPvhquu616ryleNFeRpU+zhkC/otM61/IpC0fCKQr1XLN+xe/CqIRf +cA4/OeJ5HFAmzYdCYoaK9yOWmJabxBZTuEvJ41cPSjq2Pi/f9AC0QCulDveoE8ET +luaP/ILcg8VCcRBdg4ls3oLpkCVtUjcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAQ +U0ekppGXa0IGfo8l4UZWrqBOhDjbrOh2LcSBU0oiHB/pWHAliDy8hqXQA3rr17ir +cm458oUFupEHvB26yQiO1uNLQcIuXTiGoUA2xcepgjYbZahn1WbYTJuKFtcNxkOV +sq+Dmj34yvc1Ror4lcqlg5foPE8aH2ONroG2PwM7eQ== +-----END CERTIFICATE----- diff --git a/test/certs.js b/test/certs.js index edcf8ff..8d34501 100644 --- a/test/certs.js +++ b/test/certs.js @@ -1,4 +1,4 @@ -// Copyright 2016 Joyent, Inc. All rights reserved. +// Copyright 2018 Joyent, Inc. All rights reserved. var test = require('tape').test; @@ -14,7 +14,7 @@ var testDir = path.join(__dirname, 'assets'); var GEORGE_KEY, GEORGE_SSH, GEORGE_X509; var BARRY_KEY; -var JIM_KEY, JIM_SSH, JIM_X509; +var JIM_KEY, JIM_SSH, JIM_X509, JIM_X509_TXT; var EC_KEY, EC_KEY2; var SUE_KEY; @@ -32,6 +32,7 @@ test('setup', function (t) { JIM_SSH = fs.readFileSync(path.join(testDir, 'jim-openssh.pub')); JIM_X509 = fs.readFileSync(path.join(testDir, 'jim-x509.pem')); + JIM_X509_TXT = fs.readFileSync(path.join(testDir, 'jim-x509-text.pem')); d = fs.readFileSync(path.join(testDir, 'id_ecdsa')); EC_KEY = sshpk.parsePrivateKey(d); @@ -136,6 +137,14 @@ test('rsa x509 cert self-signed', function (t) { t.end(); }); +test('x509 pem cert with extra text', function (t) { + var cert = sshpk.parseCertificate(JIM_X509_TXT, 'pem'); + t.ok(sshpk.Certificate.isCertificate(cert)); + t.ok(JIM_KEY.fingerprint().matches(cert.subjectKey)); + t.ok(cert.isSignedByKey(JIM_KEY)); + t.end(); +}); + test('create rsa self-signed, loopback', function (t) { var id = sshpk.identityForHost('foobar.com'); var cert = sshpk.createSelfSignedCertificate(id, JIM_KEY); diff --git a/test/openssl-cmd.js b/test/openssl-cmd.js index d1472b6..dfcbfbb 100644 --- a/test/openssl-cmd.js +++ b/test/openssl-cmd.js @@ -1,4 +1,4 @@ -// Copyright 2017 Joyent, Inc. All rights reserved. +// Copyright 2018 Joyent, Inc. All rights reserved. var test = require('tape').test; var sshpk = require('../lib/index'); @@ -392,6 +392,32 @@ function genTests() { kid.stdin.end(); }); + test('make a self-signed cert, openssl x509 -text parse', function (t) { + var pem = fs.readFileSync(path.join(testDir, 'id_' + algo)); + var key = sshpk.parsePrivateKey(pem, 'pkcs1'); + + var ids = [ + sshpk.identityFromDN('cn=' + algo + ', c=US'), + sshpk.identityFromDN('cn=' + algo + '.test, c=AU') + ]; + var cert = sshpk.createSelfSignedCertificate(ids, key); + var certPem = cert.toBuffer('pem'); + + var kid = spawn('openssl', ['x509', '-text']); + var bufs = []; + kid.stdout.on('data', bufs.push.bind(bufs)); + kid.on('close', function (rc) { + t.equal(rc, 0); + var output = Buffer.concat(bufs).toString(); + + var cert2 = sshpk.parseCertificate(output, 'pem'); + t.ok(cert2.fingerprint('sha512').matches(cert)); + t.end(); + }); + kid.stdin.write(certPem); + kid.stdin.end(); + }); + test('make a self-signed cert with generated key', function (t) { if (algo !== 'ecdsa') { t.end();