UNPKG

azure-cli

Version:

Microsoft Azure Cross Platform Command Line tool

501 lines (404 loc) 14.8 kB
// // Copyright (c) Microsoft and contributors. 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 der = require('./der'); var util = require('util'); var assert = require('assert'); var crypto = require('crypto'); var Decoder = der.Decoder; var Pkcs = { PfxDecoder: PfxDecoder, oid: { '1.2.840.113549.1':'pkcs', '1.2.840.113549.1.1':'pkcs1', '1.2.840.113549.1.1.1':'RSA encryption', '1.2.840.113549.1.7':'pkcs7', '1.2.840.113549.1.7.1':'data', '1.2.840.113549.1.7.6':'encryptedData', '1.2.840.113549.1.9':'pkcs9', '1.2.840.113549.1.9.20':'friendlyName', '1.2.840.113549.1.9.21':'localKeyId', '1.2.840.113549.1.9.22':'certTypes', '1.2.840.113549.1.9.22.1':'x509Certificate', '1.2.840.113549.1.12':'pkcs12', '1.2.840.113549.1.12.1':'Password-Based Encryption identifiers', '1.2.840.113549.1.12.1.3':'pbeWithSHAAnd3-KeyTripleDES-CBC', '1.2.840.113549.1.12.1.6':'pbeWithSHAAnd40BitRC2-CBC', '1.2.840.113549.1.12.10.1':'Bag Types', '1.2.840.113549.1.12.10.1.2':'pkcs-8ShroudedKeyBag', '1.2.840.113549.1.12.10.1.3':'certBag', '1.3.6.1.4.1.311.17.1':'pkcs-12-key-provider-name-attr' }, pfx2pem: pfx2pem }; function pfx2pem(buffer) { var pem = ''; var decoder = new PfxDecoder(); decoder.on('key', function (key) { pem = pem + '-----BEGIN RSA PRIVATE KEY-----\r\n' + split(key.buffer.toString('base64')) + '\r\n' + '-----END RSA PRIVATE KEY-----\r\n'; }); decoder.on('cert', function (cert) { pem = pem + '-----BEGIN CERTIFICATE-----\r\n' + split(cert.buffer.toString('base64')) + '\r\n' + '-----END CERTIFICATE-----\r\n'; }); decoder.parse(buffer); function split(text) { var index = 0; var result = ''; while (index + 64 < text.length) { result = result + text.substring(index, index + 64) + '\r\n'; index += 64; } return result + text.substring(index); } return new Buffer(pem); } function PfxDecoder() { var self = this; Decoder.call(self); var pdu = new PfxPduDecoder(); self.on('element', function(element) { pdu.parse(element.buffer); }); pdu.on('cert', function(cert) { self.emit('cert', cert); }); pdu.on('key', function(key) { self.emit('key', key); }); } util.inherits(PfxDecoder, Decoder); function PfxPduDecoder() { var self = this; Decoder.call(self); var authSafe = new PfxContentInfoDecoder(); var authSafeItems = new PfxContentBagDecoder(); var safeBag = new PfxSafeBagDecoder(); self.on('end', function(elements) { authSafe.parse(elements[1].buffer); }); authSafe.on('data', function(data) { authSafeItems.parse(data); }); authSafeItems.on('data', function(data) { safeBag.parse(data); }); safeBag.on('cert', function(cert) { self.emit('cert', cert); }); safeBag.on('key', function(key) { self.emit('key', key); }); } util.inherits(PfxPduDecoder, Decoder); function PfxContentBagDecoder() { var self = this; Decoder.call(self); var itemDecoder = new PfxContentInfoDecoder(); itemDecoder.on('data', function(data) { self.emit('data', data); }); self.on('element', function(element) { assert.equal(element.tag, der.SEQUENCE); itemDecoder.parse(element.buffer); }); } util.inherits(PfxContentBagDecoder, Decoder); function PfxContentInfoDecoder() { var self = this; Decoder.call(self); var generic = new Decoder(); var encryptedData = new PfxEncryptedDataDecoder(); encryptedData.on('data', function(data) { self.emit('data', data); }); self.on('end', function(elements) { assert.equal(elements[0].tag, der.OBJECT_IDENTIFIER); var oid = elements[0].value; if (Pkcs.oid[oid] == 'data') { assert.equal(elements.length, 2); assert.equal(elements[1].tag, der.CONTEXT_CONSTRUCTED_0); elements = generic.parse(elements[1].buffer); assert.equal(elements.length, 1); assert.equal(elements[0].tag, der.OCTET_STRING); elements = generic.parse(elements[0].buffer); for(var index = 0; index != elements.length; ++index) { assert.equal(elements[index].tag, der.SEQUENCE); self.emit('data', elements[index].buffer); } } else if (Pkcs.oid[oid] == 'encryptedData') { assert.equal(elements[1].tag, der.CONTEXT_CONSTRUCTED_0); elements = generic.parse(elements[1].buffer); assert.equal(elements.length, 1); assert.equal(elements[0].tag, der.SEQUENCE); elements = generic.parse(elements[0].buffer); assert.equal(elements.length, 2); assert.equal(elements[0].tag, der.INTEGER); assert.equal(elements[0].value, 0); assert.equal(elements[1].tag, der.SEQUENCE); encryptedData.parse(elements[1].buffer); } else { throw new Error('Unknown AuthenticatedSafe oid ' + oid); } }); } util.inherits(PfxContentInfoDecoder, Decoder); function PfxEncryptedDataDecoder() { var self = this; Decoder.call(self); var algorithmDecoder = new PfxAlgorithmDecoder(); var generic = new Decoder(); self.on('end', function(elements) { assert.equal(elements.length, 3); assert.equal(elements[0].tag, der.OBJECT_IDENTIFIER); assert.equal(elements[1].tag, der.SEQUENCE); assert.equal(elements[2].tag, der.CONTEXT_PRIMATIVE_0); var algorithm = algorithmDecoder.parse(elements[1].buffer); var cipher = algorithm.createCipher(null); var data1 = cipher.update(elements[2].buffer); var data2 = cipher.final(); var data = null; if (Buffer.concat) { var data1buf = new Buffer(data1, 'binary'); var data2buf = new Buffer(data2, 'binary'); data = Buffer.concat([data1buf, data2buf]); } else { data = new Buffer(data1 + data2, 'binary'); } elements = generic.parse(data); for(var index = 0; index != elements.length; ++index) { assert.equal(elements[index].tag, der.SEQUENCE); self.emit('data', elements[index].buffer); } }); } util.inherits(PfxEncryptedDataDecoder, Decoder); function PfxAlgorithmDecoder() { var self = this; Decoder.call(self); var generic = new Decoder(); self.on('end', function(elements) { assert.equal(elements.length, 2); assert.equal(elements[0].tag, der.OBJECT_IDENTIFIER); assert.equal(elements[1].tag, der.SEQUENCE); var args = generic.parse(elements[1].buffer); assert.equal(args.length, 2); assert.equal(args[0].tag, der.OCTET_STRING); assert.equal(args[1].tag, der.INTEGER); var algorithmOid = elements[0].value; var salt = args[0].value; var iterations = args[1].value; var algorithmName = Pkcs.oid[algorithmOid]; if (algorithmName == 'pbeWithSHAAnd40BitRC2-CBC') { elements.createCipher = function(password) { var key = createPkcs12Info(password, salt, 5, iterations, 1); var iv = createPkcs12Info(password, salt, 8, iterations, 2); return crypto.createDecipheriv('rc2-40-cbc', key, iv); }; } else if (algorithmName == 'pbeWithSHAAnd3-KeyTripleDES-CBC') { elements.createCipher = function(password) { var key = createPkcs12Info(password, salt, 24, iterations, 1); var iv = createPkcs12Info(password, salt, 8, iterations, 2); return crypto.createDecipheriv('des-ede3-cbc', key, iv); }; } else { throw new Error('Unknown algorithmId ' + algorithmOid); } }); } util.inherits(PfxAlgorithmDecoder, Decoder); function PfxSafeBagDecoder() { var self = this; Decoder.call(self); var generic = new Decoder(); var pkcs8ShroudedKeyBag = new PfxPkcs8ShroudedKeyBagDecoder(); var certBag = new PfxCertBagDecoder(); certBag.on('cert', function(cert){self.emit('cert', cert);}); pkcs8ShroudedKeyBag.on('key', function(key){self.emit('key', key);}); self.on('element', function(element) { assert.equal(element.tag, der.SEQUENCE); var elements = generic.parse(element.buffer); assert.equal(elements[0].tag, der.OBJECT_IDENTIFIER); var bagId = elements[0].value; if (Pkcs.oid[bagId] == 'pkcs-8ShroudedKeyBag') { assert.equal(elements[1].tag, der.CONTEXT_CONSTRUCTED_0); elements = generic.parse(elements[1].buffer); assert.equal(elements[0].tag, der.SEQUENCE); pkcs8ShroudedKeyBag.parse(elements[0].buffer); } else if (Pkcs.oid[bagId] == 'certBag') { assert.equal(elements[1].tag, der.CONTEXT_CONSTRUCTED_0); elements = generic.parse(elements[1].buffer); assert.equal(elements[0].tag, der.SEQUENCE); certBag.parse(elements[0].buffer); } else { throw new Error('Unknown SafeBag bagId ' + bagId); } }); } util.inherits(PfxSafeBagDecoder, Decoder); function PfxPkcs8ShroudedKeyBagDecoder() { var self = this; Decoder.call(self); var generic = new Decoder(); var algorithmDecoder = new PfxAlgorithmDecoder(); self.on('end', function(elements) { assert.equal(elements.length, 2); assert.equal(elements[0].tag, der.SEQUENCE); assert.equal(elements[1].tag, der.OCTET_STRING); var algorithm = algorithmDecoder.parse(elements[0].buffer); var cipher = algorithm.createCipher(null); var data1 = cipher.update(elements[1].buffer); var data2 = cipher.final(); var data = null; if (Buffer.concat) { var data1buf = new Buffer(data1, 'binary'); var data2buf = new Buffer(data2, 'binary'); data = Buffer.concat([data1buf, data2buf]); } else { data = new Buffer(data1 + data2, 'binary'); } elements = generic.parse(data); for(var index = 0; index != elements.length; ++index) { assert.equal(elements[index].tag, der.SEQUENCE); var fields = generic.parse(elements[index].buffer); assert.equal(fields.length, 3); assert.equal(fields[0].tag, der.INTEGER); assert.equal(fields[1].tag, der.SEQUENCE); fields[1].fields = generic.parse(fields[1].buffer); assert.equal(fields[1].fields[0].tag, der.OBJECT_IDENTIFIER); assert.equal(fields[2].tag, der.OCTET_STRING); self.emit('key', { type: fields[1].fields[0].value, buffer: fields[2].buffer }); } }); } util.inherits(PfxPkcs8ShroudedKeyBagDecoder, Decoder); function PfxCertBagDecoder() { var self = this; Decoder.call(self); var generic = new Decoder(); self.on('end', function(elements) { assert.equal(elements[0].tag, der.OBJECT_IDENTIFIER); var certId = elements[0].value; if (Pkcs.oid[certId] == 'x509Certificate') { assert.equal(elements[1].tag, der.CONTEXT_CONSTRUCTED_0); elements = generic.parse(elements[1].buffer); assert.equal(elements[0].tag, der.OCTET_STRING); self.emit('cert', { type: certId, buffer: elements[0].buffer }); } else { throw new Error('Unknown CertBag certId ' + certId); } }); } util.inherits(PfxCertBagDecoder, Decoder); function createPkcs12Info(password, salt, keyLength, iterations, id) { var digestLength = 20; // sha-1 digest size constant var inputBytes = 64; // pkcs12 constant for sha-1 var idString = new Buffer(inputBytes); for(var index = 0; index != idString.length; ++index) { idString[index] = id; } var saltString = new Buffer(strangeLength(salt.length, inputBytes)); fillBuffer(salt, saltString); var passwordString = null; if (password === null || password.length === 0) { passwordString = new Buffer(0); } else { passwordString = new Buffer(strangeLength(password.length, inputBytes)); fillBuffer(password, passwordString); } var inputString = new Buffer(saltString.length + passwordString.length); saltString.copy(inputString); passwordString.copy(inputString, saltString.length); var blocks = strangeLength(keyLength, digestLength) / digestLength; var outputString = new Buffer(digestLength); var holdString = new Buffer(inputBytes); var dataString = new Buffer(blocks * digestLength); var offset = 0; for(var block = 0; block != blocks; ++block) { outputString = digestIterations(idString, inputString, iterations); outputString.copy(dataString, offset); offset += outputString.length; if (block != blocks - 1) { fillBuffer(outputString, holdString); createInputString(inputString, holdString, holdString); } } var info = new Buffer(keyLength); dataString.copy(info, 0, 0, keyLength); return info; } function digestIterations(idString, inputString, iterations) { var hash = crypto.createHash('sha1'); hash.update(idString); hash.update(inputString); // console.log('Digest D (length %d):', idString.length); // console.log(idString); // console.log('Digest I (length %d):', inputString.length); // console.log(inputString); var outputString = new Buffer(hash.digest('base64'),'base64'); for(var iteration = 1; iteration != iterations; ++iteration) { hash = crypto.createHash('sha1'); hash.update(outputString); outputString = new Buffer(hash.digest('base64'), 'base64'); } // console.log(outputString); return outputString; } function createInputString(oldInput, addedInput) { var inputSize = addedInput.length; var passes = (oldInput.length / inputSize) >> 0; var offset = 0; for (var pass = 0; pass != passes; ++pass) { var carry = 1; for (var scan = 0; scan != inputSize; ++scan) { var index = inputSize - scan - 1; var value = oldInput[index + offset] + addedInput[index] + carry; oldInput[index + offset] = value & 0xff; carry = value >> 8; } offset += inputSize; } } function strangeLength(length, blockSize) { return blockSize * (((length+blockSize-1)/blockSize) >> 0); } function fillBuffer(source, target) { var sourceLength = source.length; var targetLength = target.length; var targetStart = 0; while (targetStart + sourceLength <= targetLength) { source.copy(target, targetStart); targetStart += sourceLength; } // console.log('source.copy %d %d', targetStart, targetLength); if (targetLength != targetStart) { source.copy(target, targetStart, 0, targetLength - targetStart); } //console.log('fillBuffer'); //console.log(source); //console.log(target); } exports = module.exports = Pkcs;