UNPKG

9.4 kBJavaScriptView Raw
1/*** Generated by streamline 0.10.17 (callbacks) - DO NOT EDIT ***//**
2 * Copyright (c) Microsoft. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16var crypto = require('crypto');
17var util = require('util');
18var path = require('path');
19var fs = require('fs');
20var moment = require('moment');
21
22var utils = require('./utils');
23var utilsCore = require('./utilsCore');
24
25// x509 PEM Cert header
26var BEGIN_CERT = '-----BEGIN CERTIFICATE-----';
27// x509 PEM Cert footer
28var END_CERT = '-----END CERTIFICATE-----';
29// SSH RSA Public Key header
30var SSHDashRSA = 'ssh-rsa';
31
32// input: Open SSH RSA PublicKey
33//
34// ssh-rsa {body} {info}
35//
36// output: PEM formatted PKCS#8 RSA Public Key
37//
38// -----BEGIN PUBLIC KEY-----
39// BASE64 ENCODED PUBLICKEYINFO
40// -----END PUBLIC KEY-----
41//
42// PUBLICKEYINFO ::= SEQUENCE {
43// algorithm ::= SEQUENCE {
44// algorithm 1.2.840.113549.1.1.1
45// }
46// PublicKey ::= SEQUENCE {
47// modulus INTEGER, -- n
48// publicExponent INTEGER -- e
49// }
50// }
51//
52// 1.2.840.113549.1.1.1 is the OID for RSA
53//
54exports.openSshRSAPubToPkcs8RsaPubPEM = function (sshRSAPubKey) {
55 var sshKeyToPEM = require('ssh-key-to-pem');
56 return sshKeyToPEM(sshRSAPubKey);
57};
58
59// input: PEM formatted PKCS#8 RSA Public Key
60//
61// -----BEGIN PUBLIC KEY-----
62// BASE64 ENCODED PUBLICKEYINFO
63// -----END PUBLIC KEY-----
64//
65// PUBLICKEYINFO ::= SEQUENCE {
66// algorithm ::= SEQUENCE {
67// algorithm 1.2.840.113549.1.1.1
68// }
69// PublicKey ::= SEQUENCE {
70// modulus INTEGER, -- n
71// publicExponent INTEGER -- e
72// }
73// }
74//
75// 1.2.840.113549.1.1.1 is the OID for RSA
76//
77// output: PEM formatted X509 Certificate
78//
79// -----BEGIN CERTIFICATE-----
80// BASE64 ENCODED X509Certificate
81// -----END CERTIFICATE-----
82//
83// X509Certificate ::= SEQUENCE {
84// tbsCertificate ::= SEQUENCE {
85// version [0] v1,
86// serialNumber 1,
87// signature ::= SEQUENCE {
88// algorithm 1.2.840.113549.1.1.5
89// },
90// issuer CN=Root Agency,
91// validity ::= SEQUENCE {
92// notBefore Time,
93// notAfter Time
94// },
95// subject '/C=DE/O=dummy-subject/CN=dummy,
96// subjectPublicKeyInfo ::= SEQUENCE {
97// algorithm ::= SEQUENCE {
98// algorithm 1.2.840.113549.1.1.1
99// },
100// subjectPublicKey ::= SEQUENCE {
101// modulus INTEGER, -- n
102// publicExponent INTEGER -- e
103// }
104// }
105// },
106// signatureAlgorithm ::= SEQUENCE {
107// algorithm 1.2.840.113549.1.1.5
108// },
109// signature 00
110// }
111//
112// 1.2.840.113549.1.1.5 is the OID 'SHA-1 with RSA Encryption'
113// 1.2.840.113549.1.1.1 is the OID for RSA
114//
115// X509Certificate::signature represents the signature for the TBSCertificate
116// creating signature requires private key, since we don't have access to
117// private key we set X509Certificate::signature to 00.
118//
119exports.pkcs8RSAPubPEMToX509CertPEM = function (pkc8RSAPub) {
120 require('jsrsasign');
121
122 var certValidityRange = exports.getCertValidityRange();
123 var x509CertPEM = KJUR.asn1.x509.X509Util.newCertPEM({
124 serial: { int: 1 },
125 sigalg: { name: 'SHA1withRSA', paramempty: true },
126 issuer: { str: 'CN=Root Agency' },
127 notbefore: { 'str': certValidityRange.start },
128 notafter: { 'str': certValidityRange.end },
129 subject: { str: '/C=DE/O=dummy-subject/CN=dummy' },
130 sbjpubkey: pkc8RSAPub,
131 sighex: '00'
132 });
133
134 var certEnd = x509CertPEM.indexOf(END_CERT);
135 // Bug: jsrsasign module seems inserting an extra CRLF, remove it
136 if (x509CertPEM[certEnd - 2] === '\r' && x509CertPEM[certEnd - 1] === '\n') {
137 if (x509CertPEM[certEnd - 4] === '\r' && x509CertPEM[certEnd - 3] === '\n') {
138 x509CertPEM = x509CertPEM.replace(new RegExp('\r\n' + END_CERT + '(\r\n)?' + '$'), END_CERT);
139 }
140 } else if (x509CertPEM[certEnd - 2] === '\n' && x509CertPEM[certEnd - 1] === '\n') {
141 x509CertPEM = x509CertPEM.replace(new RegExp('\n' + END_CERT + '\n?' + '$'), END_CERT);
142 }
143
144 return x509CertPEM;
145};
146
147exports.openSshRSAPubToX509CertPEM = function (openSShRSAPub) {
148 var pkcs8RsaPubKey = exports.openSshRSAPubToPkcs8RsaPubPEM(openSShRSAPub);
149 return exports.pkcs8RSAPubPEMToX509CertPEM(pkcs8RsaPubKey);
150};
151
152exports.isX509CertPEM = function (data) {
153 return (data && data.indexOf(BEGIN_CERT) !== -1 && data.indexOf(END_CERT) !== -1);
154};
155
156exports.isOpenSshRSAPub = function (data) {
157 if (data) {
158 // should be in format 'ssh-rsa {body} {info}'
159 var tokens = data.split([' ']);
160 return (tokens.length >= 2 && tokens[0] === SSHDashRSA);
161 }
162
163 return false;
164};
165
166exports.getFingerprintFromX509CertPEM = function (x509CertPEM) {
167 var certBase64 = exports.extractBase64X509CertFromPEM(x509CertPEM);
168 // Calculate sha1 hash of the cert
169 var cert = new Buffer(certBase64, 'base64');
170 var sha1 = crypto.createHash('sha1');
171 sha1.update(cert);
172 return sha1.digest('hex');
173};
174
175exports.extractBase64X509CertFromPEM = function (x509CertPEM) {
176 // Extract the base64 encoded X509 cert out of PEM file
177 var beginCert = x509CertPEM.indexOf(BEGIN_CERT) + BEGIN_CERT.length;
178 if (x509CertPEM[beginCert] === '\n') {
179 beginCert = beginCert + 1;
180 } else if (x509CertPEM[beginCert] === '\r' && x509CertPEM[beginCert + 1] === '\n') {
181 beginCert = beginCert + 2;
182 }
183
184 var endCert = '\n' + x509CertPEM.indexOf(END_CERT);
185 if (endCert === -1) {
186 endCert = '\r\n' + x509CertPEM.indexOf(END_CERT);
187 }
188
189 return x509CertPEM.substring(beginCert, endCert);
190};
191
192exports.generatePemKeyPair = function () {
193 var jsrsasign = require('jsrsasign');
194
195 var keys = jsrsasign.KEYUTIL.generateKeypair('RSA', 2048);
196 var pub = jsrsasign.KEYUTIL.getPEM(keys.pubKeyObj);
197 var pvt = jsrsasign.KEYUTIL.getPEM(keys.prvKeyObj, 'PKCS8PRV');
198
199 return { public: pub, private: pvt };
200};
201
202exports.generateX509PemCert = function (publicKey, privateKey, password) {
203 var jsrsasign = require('jsrsasign');
204
205 var certValidityRange = exports.getCertValidityRange();
206 var certParams = {
207 serial: { int: 1 },
208 sigalg: { name: 'SHA1withECDSA', paramempty: true },
209 issuer: { str: 'CN=Root Agency' },
210 notbefore: { 'str': certValidityRange.start },
211 notafter: { 'str': certValidityRange.end },
212 subject: { str: '/C=US/O=b' },
213 sbjpubkey: publicKey
214 };
215
216 if(privateKey) {
217 certParams.cakey = [ privateKey, password ];
218 }
219
220 var certPEM = jsrsasign.asn1.x509.X509Util.newCertPEM(certParams);
221 return certPEM;
222};
223
224exports.checkSSHKeys = function (azureSshDir, keyPaths, callback) {
225 utils.fileExists(azureSshDir, function(error, exists) {
226 if (error) {
227 return callback(error);
228 }
229
230 if (!exists) {
231 fs.mkdir(azureSshDir, function(error) {
232 if (error) {
233 return callback(error);
234 }
235
236 return callback(null, false);
237 });
238 } else {
239 utils.fileExists(keyPaths.privateKeyPath, function(error, exists) {
240 if (error) {
241 return callback(error);
242 }
243
244 if (exists) {
245 utils.fileExists(keyPaths.certPath, function(error, exists) {
246 if (error) {
247 return callback(error);
248 }
249
250 return callback(null, exists);
251 });
252 } else {
253 return callback(null, false);
254 }
255 });
256 }
257 });
258};
259
260exports.generateSSHKeysIfNeeded = function (vmName, callback) {
261 var azureSshDir = path.join(utilsCore.azureDir(), 'ssh');
262 var keyPaths = {
263 certPath: path.join(azureSshDir, vmName + '-cert.pem'),
264 privateKeyPath: path.join(azureSshDir, vmName + '-key.pem')
265 };
266
267 exports.checkSSHKeys(azureSshDir, keyPaths, function(error, exists) {
268 if (!exists) {
269 exports.generateAndSaveSSHKeys(keyPaths.privateKeyPath, keyPaths.certPath);
270 }
271
272 return callback(null, keyPaths);
273 });
274};
275
276exports.generateAndSaveSSHKeys = function (privateKeyPath, certPath) {
277 var keys = exports.generatePemKeyPair();
278 var cert = exports.generateX509PemCert(keys.public, keys.private, '');
279 fs.writeFileSync(privateKeyPath, keys.private);
280 fs.writeFileSync(certPath, cert);
281 fs.chmodSync(privateKeyPath, 0600);
282};
283
284exports.getCertValidityRange = function () {
285 function pad(n) {
286 return n < 10 ? '0' + n : n;
287 }
288
289 function toUTCString(d) {
290 return util.format('%s%s%s%s%s%sZ', d.getUTCFullYear(),
291 pad(d.getUTCMonth() + 1),
292 pad(d.getUTCDate()),
293 pad(d.getUTCHours()),
294 pad(d.getUTCMinutes()),
295 pad(d.getUTCSeconds()));
296 }
297
298 var startDateTime = new Date();
299 startDateTime.setMinutes(startDateTime.getMinutes() - 10);
300 var m = moment(startDateTime);
301 m.add(10, 'years');
302 var endDateTime = new Date(m.toISOString());
303
304 return {
305 start: toUTCString(startDateTime),
306 end: toUTCString(endDateTime)
307 };
308};
\No newline at end of file