source: trip-planner-front/node_modules/sshpk/lib/formats/pem.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 7.3 KB
Line 
1// Copyright 2018 Joyent, Inc.
2
3module.exports = {
4 read: read,
5 write: write
6};
7
8var assert = require('assert-plus');
9var asn1 = require('asn1');
10var crypto = require('crypto');
11var Buffer = require('safer-buffer').Buffer;
12var algs = require('../algs');
13var utils = require('../utils');
14var Key = require('../key');
15var PrivateKey = require('../private-key');
16
17var pkcs1 = require('./pkcs1');
18var pkcs8 = require('./pkcs8');
19var sshpriv = require('./ssh-private');
20var rfc4253 = require('./rfc4253');
21
22var errors = require('../errors');
23
24var OID_PBES2 = '1.2.840.113549.1.5.13';
25var OID_PBKDF2 = '1.2.840.113549.1.5.12';
26
27var OID_TO_CIPHER = {
28 '1.2.840.113549.3.7': '3des-cbc',
29 '2.16.840.1.101.3.4.1.2': 'aes128-cbc',
30 '2.16.840.1.101.3.4.1.42': 'aes256-cbc'
31};
32var CIPHER_TO_OID = {};
33Object.keys(OID_TO_CIPHER).forEach(function (k) {
34 CIPHER_TO_OID[OID_TO_CIPHER[k]] = k;
35});
36
37var OID_TO_HASH = {
38 '1.2.840.113549.2.7': 'sha1',
39 '1.2.840.113549.2.9': 'sha256',
40 '1.2.840.113549.2.11': 'sha512'
41};
42var HASH_TO_OID = {};
43Object.keys(OID_TO_HASH).forEach(function (k) {
44 HASH_TO_OID[OID_TO_HASH[k]] = k;
45});
46
47/*
48 * For reading we support both PKCS#1 and PKCS#8. If we find a private key,
49 * we just take the public component of it and use that.
50 */
51function read(buf, options, forceType) {
52 var input = buf;
53 if (typeof (buf) !== 'string') {
54 assert.buffer(buf, 'buf');
55 buf = buf.toString('ascii');
56 }
57
58 var lines = buf.trim().split(/[\r\n]+/g);
59
60 var m;
61 var si = -1;
62 while (!m && si < lines.length) {
63 m = lines[++si].match(/*JSSTYLED*/
64 /[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
65 }
66 assert.ok(m, 'invalid PEM header');
67
68 var m2;
69 var ei = lines.length;
70 while (!m2 && ei > 0) {
71 m2 = lines[--ei].match(/*JSSTYLED*/
72 /[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
73 }
74 assert.ok(m2, 'invalid PEM footer');
75
76 /* Begin and end banners must match key type */
77 assert.equal(m[2], m2[2]);
78 var type = m[2].toLowerCase();
79
80 var alg;
81 if (m[1]) {
82 /* They also must match algorithms, if given */
83 assert.equal(m[1], m2[1], 'PEM header and footer mismatch');
84 alg = m[1].trim();
85 }
86
87 lines = lines.slice(si, ei + 1);
88
89 var headers = {};
90 while (true) {
91 lines = lines.slice(1);
92 m = lines[0].match(/*JSSTYLED*/
93 /^([A-Za-z0-9-]+): (.+)$/);
94 if (!m)
95 break;
96 headers[m[1].toLowerCase()] = m[2];
97 }
98
99 /* Chop off the first and last lines */
100 lines = lines.slice(0, -1).join('');
101 buf = Buffer.from(lines, 'base64');
102
103 var cipher, key, iv;
104 if (headers['proc-type']) {
105 var parts = headers['proc-type'].split(',');
106 if (parts[0] === '4' && parts[1] === 'ENCRYPTED') {
107 if (typeof (options.passphrase) === 'string') {
108 options.passphrase = Buffer.from(
109 options.passphrase, 'utf-8');
110 }
111 if (!Buffer.isBuffer(options.passphrase)) {
112 throw (new errors.KeyEncryptedError(
113 options.filename, 'PEM'));
114 } else {
115 parts = headers['dek-info'].split(',');
116 assert.ok(parts.length === 2);
117 cipher = parts[0].toLowerCase();
118 iv = Buffer.from(parts[1], 'hex');
119 key = utils.opensslKeyDeriv(cipher, iv,
120 options.passphrase, 1).key;
121 }
122 }
123 }
124
125 if (alg && alg.toLowerCase() === 'encrypted') {
126 var eder = new asn1.BerReader(buf);
127 var pbesEnd;
128 eder.readSequence();
129
130 eder.readSequence();
131 pbesEnd = eder.offset + eder.length;
132
133 var method = eder.readOID();
134 if (method !== OID_PBES2) {
135 throw (new Error('Unsupported PEM/PKCS8 encryption ' +
136 'scheme: ' + method));
137 }
138
139 eder.readSequence(); /* PBES2-params */
140
141 eder.readSequence(); /* keyDerivationFunc */
142 var kdfEnd = eder.offset + eder.length;
143 var kdfOid = eder.readOID();
144 if (kdfOid !== OID_PBKDF2)
145 throw (new Error('Unsupported PBES2 KDF: ' + kdfOid));
146 eder.readSequence();
147 var salt = eder.readString(asn1.Ber.OctetString, true);
148 var iterations = eder.readInt();
149 var hashAlg = 'sha1';
150 if (eder.offset < kdfEnd) {
151 eder.readSequence();
152 var hashAlgOid = eder.readOID();
153 hashAlg = OID_TO_HASH[hashAlgOid];
154 if (hashAlg === undefined) {
155 throw (new Error('Unsupported PBKDF2 hash: ' +
156 hashAlgOid));
157 }
158 }
159 eder._offset = kdfEnd;
160
161 eder.readSequence(); /* encryptionScheme */
162 var cipherOid = eder.readOID();
163 cipher = OID_TO_CIPHER[cipherOid];
164 if (cipher === undefined) {
165 throw (new Error('Unsupported PBES2 cipher: ' +
166 cipherOid));
167 }
168 iv = eder.readString(asn1.Ber.OctetString, true);
169
170 eder._offset = pbesEnd;
171 buf = eder.readString(asn1.Ber.OctetString, true);
172
173 if (typeof (options.passphrase) === 'string') {
174 options.passphrase = Buffer.from(
175 options.passphrase, 'utf-8');
176 }
177 if (!Buffer.isBuffer(options.passphrase)) {
178 throw (new errors.KeyEncryptedError(
179 options.filename, 'PEM'));
180 }
181
182 var cinfo = utils.opensshCipherInfo(cipher);
183
184 cipher = cinfo.opensslName;
185 key = utils.pbkdf2(hashAlg, salt, iterations, cinfo.keySize,
186 options.passphrase);
187 alg = undefined;
188 }
189
190 if (cipher && key && iv) {
191 var cipherStream = crypto.createDecipheriv(cipher, key, iv);
192 var chunk, chunks = [];
193 cipherStream.once('error', function (e) {
194 if (e.toString().indexOf('bad decrypt') !== -1) {
195 throw (new Error('Incorrect passphrase ' +
196 'supplied, could not decrypt key'));
197 }
198 throw (e);
199 });
200 cipherStream.write(buf);
201 cipherStream.end();
202 while ((chunk = cipherStream.read()) !== null)
203 chunks.push(chunk);
204 buf = Buffer.concat(chunks);
205 }
206
207 /* The new OpenSSH internal format abuses PEM headers */
208 if (alg && alg.toLowerCase() === 'openssh')
209 return (sshpriv.readSSHPrivate(type, buf, options));
210 if (alg && alg.toLowerCase() === 'ssh2')
211 return (rfc4253.readType(type, buf, options));
212
213 var der = new asn1.BerReader(buf);
214 der.originalInput = input;
215
216 /*
217 * All of the PEM file types start with a sequence tag, so chop it
218 * off here
219 */
220 der.readSequence();
221
222 /* PKCS#1 type keys name an algorithm in the banner explicitly */
223 if (alg) {
224 if (forceType)
225 assert.strictEqual(forceType, 'pkcs1');
226 return (pkcs1.readPkcs1(alg, type, der));
227 } else {
228 if (forceType)
229 assert.strictEqual(forceType, 'pkcs8');
230 return (pkcs8.readPkcs8(alg, type, der));
231 }
232}
233
234function write(key, options, type) {
235 assert.object(key);
236
237 var alg = {
238 'ecdsa': 'EC',
239 'rsa': 'RSA',
240 'dsa': 'DSA',
241 'ed25519': 'EdDSA'
242 }[key.type];
243 var header;
244
245 var der = new asn1.BerWriter();
246
247 if (PrivateKey.isPrivateKey(key)) {
248 if (type && type === 'pkcs8') {
249 header = 'PRIVATE KEY';
250 pkcs8.writePkcs8(der, key);
251 } else {
252 if (type)
253 assert.strictEqual(type, 'pkcs1');
254 header = alg + ' PRIVATE KEY';
255 pkcs1.writePkcs1(der, key);
256 }
257
258 } else if (Key.isKey(key)) {
259 if (type && type === 'pkcs1') {
260 header = alg + ' PUBLIC KEY';
261 pkcs1.writePkcs1(der, key);
262 } else {
263 if (type)
264 assert.strictEqual(type, 'pkcs8');
265 header = 'PUBLIC KEY';
266 pkcs8.writePkcs8(der, key);
267 }
268
269 } else {
270 throw (new Error('key is not a Key or PrivateKey'));
271 }
272
273 var tmp = der.buffer.toString('base64');
274 var len = tmp.length + (tmp.length / 64) +
275 18 + 16 + header.length*2 + 10;
276 var buf = Buffer.alloc(len);
277 var o = 0;
278 o += buf.write('-----BEGIN ' + header + '-----\n', o);
279 for (var i = 0; i < tmp.length; ) {
280 var limit = i + 64;
281 if (limit > tmp.length)
282 limit = tmp.length;
283 o += buf.write(tmp.slice(i, limit), o);
284 buf[o++] = 10;
285 i = limit;
286 }
287 o += buf.write('-----END ' + header + '-----\n', o);
288
289 return (buf.slice(0, o));
290}
Note: See TracBrowser for help on using the repository browser.