Skip to content

Commit 385ff11

Browse files
author
Alex Wilson
committed
#48 wish: add support for x509 certificates in text form
Reviewed by: Cody Peter Mello <[email protected]>
1 parent c7a6c68 commit 385ff11

File tree

5 files changed

+114
-13
lines changed

5 files changed

+114
-13
lines changed

lib/formats/pem.js

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2015 Joyent, Inc.
1+
// Copyright 2018 Joyent, Inc.
22

33
module.exports = {
44
read: read,
@@ -32,14 +32,22 @@ function read(buf, options, forceType) {
3232
buf = buf.toString('ascii');
3333
}
3434

35-
var lines = buf.trim().split('\n');
35+
var lines = buf.trim().split(/[\r\n]+/g);
3636

37-
var m = lines[0].match(/*JSSTYLED*/
38-
/[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
37+
var m;
38+
var si = -1;
39+
while (!m && si < lines.length) {
40+
m = lines[++si].match(/*JSSTYLED*/
41+
/[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
42+
}
3943
assert.ok(m, 'invalid PEM header');
4044

41-
var m2 = lines[lines.length - 1].match(/*JSSTYLED*/
42-
/[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
45+
var m2;
46+
var ei = lines.length;
47+
while (!m2 && ei > 0) {
48+
m2 = lines[--ei].match(/*JSSTYLED*/
49+
/[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
50+
}
4351
assert.ok(m2, 'invalid PEM footer');
4452

4553
/* Begin and end banners must match key type */
@@ -53,6 +61,8 @@ function read(buf, options, forceType) {
5361
alg = m[1].trim();
5462
}
5563

64+
lines = lines.slice(si, ei + 1);
65+
5666
var headers = {};
5767
while (true) {
5868
lines = lines.slice(1);

lib/formats/x509-pem.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,24 @@ function read(buf, options) {
2929

3030
var lines = buf.trim().split(/[\r\n]+/g);
3131

32-
var m = lines[0].match(/*JSSTYLED*/
33-
/[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/);
32+
var m;
33+
var si = -1;
34+
while (!m && si < lines.length) {
35+
m = lines[++si].match(/*JSSTYLED*/
36+
/[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/);
37+
}
3438
assert.ok(m, 'invalid PEM header');
3539

36-
var m2 = lines[lines.length - 1].match(/*JSSTYLED*/
37-
/[-]+[ ]*END CERTIFICATE[ ]*[-]+/);
40+
var m2;
41+
var ei = lines.length;
42+
while (!m2 && ei > 0) {
43+
m2 = lines[--ei].match(/*JSSTYLED*/
44+
/[-]+[ ]*END CERTIFICATE[ ]*[-]+/);
45+
}
3846
assert.ok(m2, 'invalid PEM footer');
3947

48+
lines = lines.slice(si, ei + 1);
49+
4050
var headers = {};
4151
while (true) {
4252
lines = lines.slice(1);

test/assets/jim-x509-text.pem

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Certificate:
2+
Data:
3+
Version: 1 (0x0)
4+
Serial Number:
5+
ab:fd:52:3c:3e:52:c0:7b
6+
Signature Algorithm: sha1WithRSAEncryption
7+
Issuer: DC = jim, DC = com
8+
Validity
9+
Not Before: Jul 22 01:04:26 2016 GMT
10+
Not After : Jul 22 01:04:26 2017 GMT
11+
Subject: DC = jim, DC = com
12+
Subject Public Key Info:
13+
Public Key Algorithm: rsaEncryption
14+
RSA Public-Key: (1024 bit)
15+
Modulus:
16+
00:d5:9a:5d:d8:0d:5a:a2:98:5c:95:80:af:2b:80:
17+
41:22:b4:0f:be:1a:ae:bb:ad:7a:af:29:5e:34:57:
18+
91:a5:4f:b3:86:40:bf:a2:d3:3a:d7:f2:29:0b:47:
19+
c2:29:0a:f5:5c:b3:7e:c5:ef:c2:a8:84:5f:70:0e:
20+
3f:39:e2:79:1c:50:26:cd:87:42:62:86:8a:f7:23:
21+
96:98:96:9b:c4:16:53:b8:4b:c9:e3:57:0f:4a:3a:
22+
b6:3e:2f:df:f4:00:b4:40:2b:a5:0e:f7:a8:13:c1:
23+
13:96:e6:8f:fc:82:dc:83:c5:42:71:10:5d:83:89:
24+
6c:de:82:e9:90:25:6d:52:37
25+
Exponent: 65537 (0x10001)
26+
Signature Algorithm: sha1WithRSAEncryption
27+
10:53:47:a4:a6:91:97:6b:42:06:7e:8f:25:e1:46:56:ae:a0:
28+
4e:84:38:db:ac:e8:76:2d:c4:81:53:4a:22:1c:1f:e9:58:70:
29+
25:88:3c:bc:86:a5:d0:03:7a:eb:d7:b8:ab:72:6e:39:f2:85:
30+
05:ba:91:07:bc:1d:ba:c9:08:8e:d6:e3:4b:41:c2:2e:5d:38:
31+
86:a1:40:36:c5:c7:a9:82:36:1b:65:a8:67:d5:66:d8:4c:9b:
32+
8a:16:d7:0d:c6:43:95:b2:af:83:9a:3d:f8:ca:f7:35:46:8a:
33+
f8:95:ca:a5:83:97:e8:3c:4f:1a:1f:63:8d:ae:81:b6:3f:03:
34+
3b:79
35+
-----BEGIN CERTIFICATE-----
36+
MIIByzCCATQCCQCr/VI8PlLAezANBgkqhkiG9w0BAQUFADAqMRMwEQYKCZImiZPy
37+
LGQBGRYDamltMRMwEQYKCZImiZPyLGQBGRYDY29tMB4XDTE2MDcyMjAxMDQyNloX
38+
DTE3MDcyMjAxMDQyNlowKjETMBEGCgmSJomT8ixkARkWA2ppbTETMBEGCgmSJomT
39+
8ixkARkWA2NvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1Zpd2A1aophc
40+
lYCvK4BBIrQPvhquu616ryleNFeRpU+zhkC/otM61/IpC0fCKQr1XLN+xe/CqIRf
41+
cA4/OeJ5HFAmzYdCYoaK9yOWmJabxBZTuEvJ41cPSjq2Pi/f9AC0QCulDveoE8ET
42+
luaP/ILcg8VCcRBdg4ls3oLpkCVtUjcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAQ
43+
U0ekppGXa0IGfo8l4UZWrqBOhDjbrOh2LcSBU0oiHB/pWHAliDy8hqXQA3rr17ir
44+
cm458oUFupEHvB26yQiO1uNLQcIuXTiGoUA2xcepgjYbZahn1WbYTJuKFtcNxkOV
45+
sq+Dmj34yvc1Ror4lcqlg5foPE8aH2ONroG2PwM7eQ==
46+
-----END CERTIFICATE-----

test/certs.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2016 Joyent, Inc. All rights reserved.
1+
// Copyright 2018 Joyent, Inc. All rights reserved.
22

33
var test = require('tape').test;
44

@@ -14,7 +14,7 @@ var testDir = path.join(__dirname, 'assets');
1414

1515
var GEORGE_KEY, GEORGE_SSH, GEORGE_X509;
1616
var BARRY_KEY;
17-
var JIM_KEY, JIM_SSH, JIM_X509;
17+
var JIM_KEY, JIM_SSH, JIM_X509, JIM_X509_TXT;
1818
var EC_KEY, EC_KEY2;
1919
var SUE_KEY;
2020

@@ -32,6 +32,7 @@ test('setup', function (t) {
3232

3333
JIM_SSH = fs.readFileSync(path.join(testDir, 'jim-openssh.pub'));
3434
JIM_X509 = fs.readFileSync(path.join(testDir, 'jim-x509.pem'));
35+
JIM_X509_TXT = fs.readFileSync(path.join(testDir, 'jim-x509-text.pem'));
3536

3637
d = fs.readFileSync(path.join(testDir, 'id_ecdsa'));
3738
EC_KEY = sshpk.parsePrivateKey(d);
@@ -136,6 +137,14 @@ test('rsa x509 cert self-signed', function (t) {
136137
t.end();
137138
});
138139

140+
test('x509 pem cert with extra text', function (t) {
141+
var cert = sshpk.parseCertificate(JIM_X509_TXT, 'pem');
142+
t.ok(sshpk.Certificate.isCertificate(cert));
143+
t.ok(JIM_KEY.fingerprint().matches(cert.subjectKey));
144+
t.ok(cert.isSignedByKey(JIM_KEY));
145+
t.end();
146+
});
147+
139148
test('create rsa self-signed, loopback', function (t) {
140149
var id = sshpk.identityForHost('foobar.com');
141150
var cert = sshpk.createSelfSignedCertificate(id, JIM_KEY);

test/openssl-cmd.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 Joyent, Inc. All rights reserved.
1+
// Copyright 2018 Joyent, Inc. All rights reserved.
22

33
var test = require('tape').test;
44
var sshpk = require('../lib/index');
@@ -392,6 +392,32 @@ function genTests() {
392392
kid.stdin.end();
393393
});
394394

395+
test('make a self-signed cert, openssl x509 -text parse', function (t) {
396+
var pem = fs.readFileSync(path.join(testDir, 'id_' + algo));
397+
var key = sshpk.parsePrivateKey(pem, 'pkcs1');
398+
399+
var ids = [
400+
sshpk.identityFromDN('cn=' + algo + ', c=US'),
401+
sshpk.identityFromDN('cn=' + algo + '.test, c=AU')
402+
];
403+
var cert = sshpk.createSelfSignedCertificate(ids, key);
404+
var certPem = cert.toBuffer('pem');
405+
406+
var kid = spawn('openssl', ['x509', '-text']);
407+
var bufs = [];
408+
kid.stdout.on('data', bufs.push.bind(bufs));
409+
kid.on('close', function (rc) {
410+
t.equal(rc, 0);
411+
var output = Buffer.concat(bufs).toString();
412+
413+
var cert2 = sshpk.parseCertificate(output, 'pem');
414+
t.ok(cert2.fingerprint('sha512').matches(cert));
415+
t.end();
416+
});
417+
kid.stdin.write(certPem);
418+
kid.stdin.end();
419+
});
420+
395421
test('make a self-signed cert with generated key', function (t) {
396422
if (algo !== 'ecdsa') {
397423
t.end();

0 commit comments

Comments
 (0)