azure-cli
Version:
Microsoft Azure Cross Platform Command Line tool
308 lines (272 loc) • 9.4 kB
JavaScript
/*** Generated by streamline 0.10.17 (callbacks) - DO NOT EDIT ***//**
* Copyright (c) Microsoft. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var crypto = require('crypto');
var util = require('util');
var path = require('path');
var fs = require('fs');
var moment = require('moment');
var utils = require('./utils');
var utilsCore = require('./utilsCore');
// x509 PEM Cert header
var BEGIN_CERT = '-----BEGIN CERTIFICATE-----';
// x509 PEM Cert footer
var END_CERT = '-----END CERTIFICATE-----';
// SSH RSA Public Key header
var SSHDashRSA = 'ssh-rsa';
// input: Open SSH RSA PublicKey
//
// ssh-rsa {body} {info}
//
// output: PEM formatted PKCS#8 RSA Public Key
//
// -----BEGIN PUBLIC KEY-----
// BASE64 ENCODED PUBLICKEYINFO
// -----END PUBLIC KEY-----
//
// PUBLICKEYINFO ::= SEQUENCE {
// algorithm ::= SEQUENCE {
// algorithm 1.2.840.113549.1.1.1
// }
// PublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
// }
//
// 1.2.840.113549.1.1.1 is the OID for RSA
//
exports.openSshRSAPubToPkcs8RsaPubPEM = function (sshRSAPubKey) {
var sshKeyToPEM = require('ssh-key-to-pem');
return sshKeyToPEM(sshRSAPubKey);
};
// input: PEM formatted PKCS#8 RSA Public Key
//
// -----BEGIN PUBLIC KEY-----
// BASE64 ENCODED PUBLICKEYINFO
// -----END PUBLIC KEY-----
//
// PUBLICKEYINFO ::= SEQUENCE {
// algorithm ::= SEQUENCE {
// algorithm 1.2.840.113549.1.1.1
// }
// PublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
// }
//
// 1.2.840.113549.1.1.1 is the OID for RSA
//
// output: PEM formatted X509 Certificate
//
// -----BEGIN CERTIFICATE-----
// BASE64 ENCODED X509Certificate
// -----END CERTIFICATE-----
//
// X509Certificate ::= SEQUENCE {
// tbsCertificate ::= SEQUENCE {
// version [0] v1,
// serialNumber 1,
// signature ::= SEQUENCE {
// algorithm 1.2.840.113549.1.1.5
// },
// issuer CN=Root Agency,
// validity ::= SEQUENCE {
// notBefore Time,
// notAfter Time
// },
// subject '/C=DE/O=dummy-subject/CN=dummy,
// subjectPublicKeyInfo ::= SEQUENCE {
// algorithm ::= SEQUENCE {
// algorithm 1.2.840.113549.1.1.1
// },
// subjectPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
// }
// },
// signatureAlgorithm ::= SEQUENCE {
// algorithm 1.2.840.113549.1.1.5
// },
// signature 00
// }
//
// 1.2.840.113549.1.1.5 is the OID 'SHA-1 with RSA Encryption'
// 1.2.840.113549.1.1.1 is the OID for RSA
//
// X509Certificate::signature represents the signature for the TBSCertificate
// creating signature requires private key, since we don't have access to
// private key we set X509Certificate::signature to 00.
//
exports.pkcs8RSAPubPEMToX509CertPEM = function (pkc8RSAPub) {
require('jsrsasign');
var certValidityRange = exports.getCertValidityRange();
var x509CertPEM = KJUR.asn1.x509.X509Util.newCertPEM({
serial: { int: 1 },
sigalg: { name: 'SHA1withRSA', paramempty: true },
issuer: { str: 'CN=Root Agency' },
notbefore: { 'str': certValidityRange.start },
notafter: { 'str': certValidityRange.end },
subject: { str: '/C=DE/O=dummy-subject/CN=dummy' },
sbjpubkey: pkc8RSAPub,
sighex: '00'
});
var certEnd = x509CertPEM.indexOf(END_CERT);
// Bug: jsrsasign module seems inserting an extra CRLF, remove it
if (x509CertPEM[certEnd - 2] === '\r' && x509CertPEM[certEnd - 1] === '\n') {
if (x509CertPEM[certEnd - 4] === '\r' && x509CertPEM[certEnd - 3] === '\n') {
x509CertPEM = x509CertPEM.replace(new RegExp('\r\n' + END_CERT + '(\r\n)?' + '$'), END_CERT);
}
} else if (x509CertPEM[certEnd - 2] === '\n' && x509CertPEM[certEnd - 1] === '\n') {
x509CertPEM = x509CertPEM.replace(new RegExp('\n' + END_CERT + '\n?' + '$'), END_CERT);
}
return x509CertPEM;
};
exports.openSshRSAPubToX509CertPEM = function (openSShRSAPub) {
var pkcs8RsaPubKey = exports.openSshRSAPubToPkcs8RsaPubPEM(openSShRSAPub);
return exports.pkcs8RSAPubPEMToX509CertPEM(pkcs8RsaPubKey);
};
exports.isX509CertPEM = function (data) {
return (data && data.indexOf(BEGIN_CERT) !== -1 && data.indexOf(END_CERT) !== -1);
};
exports.isOpenSshRSAPub = function (data) {
if (data) {
// should be in format 'ssh-rsa {body} {info}'
var tokens = data.split([' ']);
return (tokens.length >= 2 && tokens[0] === SSHDashRSA);
}
return false;
};
exports.getFingerprintFromX509CertPEM = function (x509CertPEM) {
var certBase64 = exports.extractBase64X509CertFromPEM(x509CertPEM);
// Calculate sha1 hash of the cert
var cert = new Buffer(certBase64, 'base64');
var sha1 = crypto.createHash('sha1');
sha1.update(cert);
return sha1.digest('hex');
};
exports.extractBase64X509CertFromPEM = function (x509CertPEM) {
// Extract the base64 encoded X509 cert out of PEM file
var beginCert = x509CertPEM.indexOf(BEGIN_CERT) + BEGIN_CERT.length;
if (x509CertPEM[beginCert] === '\n') {
beginCert = beginCert + 1;
} else if (x509CertPEM[beginCert] === '\r' && x509CertPEM[beginCert + 1] === '\n') {
beginCert = beginCert + 2;
}
var endCert = '\n' + x509CertPEM.indexOf(END_CERT);
if (endCert === -1) {
endCert = '\r\n' + x509CertPEM.indexOf(END_CERT);
}
return x509CertPEM.substring(beginCert, endCert);
};
exports.generatePemKeyPair = function () {
var jsrsasign = require('jsrsasign');
var keys = jsrsasign.KEYUTIL.generateKeypair('RSA', 2048);
var pub = jsrsasign.KEYUTIL.getPEM(keys.pubKeyObj);
var pvt = jsrsasign.KEYUTIL.getPEM(keys.prvKeyObj, 'PKCS8PRV');
return { public: pub, private: pvt };
};
exports.generateX509PemCert = function (publicKey, privateKey, password) {
var jsrsasign = require('jsrsasign');
var certValidityRange = exports.getCertValidityRange();
var certParams = {
serial: { int: 1 },
sigalg: { name: 'SHA1withECDSA', paramempty: true },
issuer: { str: 'CN=Root Agency' },
notbefore: { 'str': certValidityRange.start },
notafter: { 'str': certValidityRange.end },
subject: { str: '/C=US/O=b' },
sbjpubkey: publicKey
};
if(privateKey) {
certParams.cakey = [ privateKey, password ];
}
var certPEM = jsrsasign.asn1.x509.X509Util.newCertPEM(certParams);
return certPEM;
};
exports.checkSSHKeys = function (azureSshDir, keyPaths, callback) {
utils.fileExists(azureSshDir, function(error, exists) {
if (error) {
return callback(error);
}
if (!exists) {
fs.mkdir(azureSshDir, function(error) {
if (error) {
return callback(error);
}
return callback(null, false);
});
} else {
utils.fileExists(keyPaths.privateKeyPath, function(error, exists) {
if (error) {
return callback(error);
}
if (exists) {
utils.fileExists(keyPaths.certPath, function(error, exists) {
if (error) {
return callback(error);
}
return callback(null, exists);
});
} else {
return callback(null, false);
}
});
}
});
};
exports.generateSSHKeysIfNeeded = function (vmName, callback) {
var azureSshDir = path.join(utilsCore.azureDir(), 'ssh');
var keyPaths = {
certPath: path.join(azureSshDir, vmName + '-cert.pem'),
privateKeyPath: path.join(azureSshDir, vmName + '-key.pem')
};
exports.checkSSHKeys(azureSshDir, keyPaths, function(error, exists) {
if (!exists) {
exports.generateAndSaveSSHKeys(keyPaths.privateKeyPath, keyPaths.certPath);
}
return callback(null, keyPaths);
});
};
exports.generateAndSaveSSHKeys = function (privateKeyPath, certPath) {
var keys = exports.generatePemKeyPair();
var cert = exports.generateX509PemCert(keys.public, keys.private, '');
fs.writeFileSync(privateKeyPath, keys.private);
fs.writeFileSync(certPath, cert);
fs.chmodSync(privateKeyPath, 0600);
};
exports.getCertValidityRange = function () {
function pad(n) {
return n < 10 ? '0' + n : n;
}
function toUTCString(d) {
return util.format('%s%s%s%s%s%sZ', d.getUTCFullYear(),
pad(d.getUTCMonth() + 1),
pad(d.getUTCDate()),
pad(d.getUTCHours()),
pad(d.getUTCMinutes()),
pad(d.getUTCSeconds()));
}
var startDateTime = new Date();
startDateTime.setMinutes(startDateTime.getMinutes() - 10);
var m = moment(startDateTime);
m.add(10, 'years');
var endDateTime = new Date(m.toISOString());
return {
start: toUTCString(startDateTime),
end: toUTCString(endDateTime)
};
};