azure-cli
Version:
Microsoft Azure Cross Platform Command Line tool
501 lines (404 loc) • 14.8 kB
JavaScript
//
// 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;