hexo/node_modules/ssh2-streams/lib/utils.js

818 lines
25 KiB
JavaScript

var crypto = require('crypto');
var Ber = require('asn1').Ber;
var BigInteger = require('./jsbn'); // only for converting PPK -> OpenSSL format
var SSH_TO_OPENSSL = require('./constants').SSH_TO_OPENSSL;
var RE_STREAM = /^arcfour/i;
var RE_KEY_LEN = /(.{64})/g;
// XXX the value of 2400 from dropbear is only for certain strings, not all
// strings. for example the list strings used during handshakes
var MAX_STRING_LEN = Infinity;//2400; // taken from dropbear
var PPK_IV = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
module.exports = {
iv_inc: iv_inc,
isStreamCipher: isStreamCipher,
readInt: readInt,
readString: readString,
parseKey: require('./keyParser'),
genPublicKey: genPublicKey,
convertPPKPrivate: convertPPKPrivate,
verifyPPKMAC: verifyPPKMAC,
decryptKey: decryptKey,
DSASigBERToBare: DSASigBERToBare,
DSASigBareToBER: DSASigBareToBER,
ECDSASigASN1ToSSH: ECDSASigASN1ToSSH,
ECDSASigSSHToASN1: ECDSASigSSHToASN1,
RSAKeySSHToASN1: RSAKeySSHToASN1,
DSAKeySSHToASN1: DSAKeySSHToASN1,
ECDSAKeySSHToASN1: ECDSAKeySSHToASN1
};
function iv_inc(iv) {
var n = 12;
var c = 0;
do {
--n;
c = iv[n];
if (c === 255)
iv[n] = 0;
else {
iv[n] = ++c;
return;
}
} while (n > 4);
}
function isStreamCipher(name) {
return RE_STREAM.test(name);
}
function readInt(buffer, start, stream, cb) {
var bufferLen = buffer.length;
if (start < 0 || start >= bufferLen || (bufferLen - start) < 4) {
stream && stream._cleanup(cb);
return false;
}
return buffer.readUInt32BE(start, true);
}
function DSASigBERToBare(signature) {
if (signature.length <= 40)
return signature;
// This is a quick and dirty way to get from BER encoded r and s that
// OpenSSL gives us, to just the bare values back to back (40 bytes
// total) like OpenSSH (and possibly others) are expecting
var asnReader = new Ber.Reader(signature);
asnReader.readSequence();
var r = asnReader.readString(Ber.Integer, true);
var s = asnReader.readString(Ber.Integer, true);
var rOffset = 0;
var sOffset = 0;
if (r.length < 20) {
var rNew = new Buffer(20);
r.copy(rNew, 1);
r = rNew;
r[0] = 0;
}
if (s.length < 20) {
var sNew = new Buffer(20);
s.copy(sNew, 1);
s = sNew;
s[0] = 0;
}
if (r.length > 20 && r[0] === 0x00)
rOffset = 1;
if (s.length > 20 && s[0] === 0x00)
sOffset = 1;
var newSig = new Buffer((r.length - rOffset) + (s.length - sOffset));
r.copy(newSig, 0, rOffset);
s.copy(newSig, r.length - rOffset, sOffset);
return newSig;
}
function DSASigBareToBER(signature) {
if (signature.length > 40)
return signature;
// Change bare signature r and s values to ASN.1 BER values for OpenSSL
var asnWriter = new Ber.Writer();
asnWriter.startSequence();
var r = signature.slice(0, 20);
var s = signature.slice(20);
if (r[0] & 0x80) {
var rNew = new Buffer(21);
rNew[0] = 0x00;
r.copy(rNew, 1);
r = rNew;
} else if (r[0] === 0x00 && !(r[1] & 0x80)) {
r = r.slice(1);
}
if (s[0] & 0x80) {
var sNew = new Buffer(21);
sNew[0] = 0x00;
s.copy(sNew, 1);
s = sNew;
} else if (s[0] === 0x00 && !(s[1] & 0x80)) {
s = s.slice(1);
}
asnWriter.writeBuffer(r, Ber.Integer);
asnWriter.writeBuffer(s, Ber.Integer);
asnWriter.endSequence();
return asnWriter.buffer;
}
function ECDSASigASN1ToSSH(signature) {
if (signature[0] === 0x00)
return signature;
// Convert SSH signature parameters to ASN.1 BER values for OpenSSL
var asnReader = new Ber.Reader(signature);
asnReader.readSequence();
var r = asnReader.readString(Ber.Integer, true);
var s = asnReader.readString(Ber.Integer, true);
if (r === null || s === null)
throw new Error('Invalid signature');
var newSig = new Buffer(4 + r.length + 4 + s.length);
newSig.writeUInt32BE(r.length, 0, true);
r.copy(newSig, 4);
newSig.writeUInt32BE(s.length, 4 + r.length, true);
s.copy(newSig, 4 + 4 + r.length);
return newSig;
}
function ECDSASigSSHToASN1(signature, self, callback) {
// Convert SSH signature parameters to ASN.1 BER values for OpenSSL
var r = readString(signature, 0, self, callback);
if (r === false)
return false;
var s = readString(signature, signature._pos, self, callback);
if (s === false)
return false;
var asnWriter = new Ber.Writer();
asnWriter.startSequence();
asnWriter.writeBuffer(r, Ber.Integer);
asnWriter.writeBuffer(s, Ber.Integer);
asnWriter.endSequence();
return asnWriter.buffer;
}
function RSAKeySSHToASN1(key, self, callback) {
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
var e = readString(key, key._pos, self, callback);
if (e === false)
return false;
var n = readString(key, key._pos, self, callback);
if (n === false)
return false;
var asnWriter = new Ber.Writer();
asnWriter.startSequence();
// algorithm
asnWriter.startSequence();
asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption
// algorithm parameters (RSA has none)
asnWriter.writeNull();
asnWriter.endSequence();
// subjectPublicKey
asnWriter.startSequence(Ber.BitString);
asnWriter.writeByte(0x00);
asnWriter.startSequence();
asnWriter.writeBuffer(n, Ber.Integer);
asnWriter.writeBuffer(e, Ber.Integer);
asnWriter.endSequence();
asnWriter.endSequence();
asnWriter.endSequence();
return asnWriter.buffer;
}
function DSAKeySSHToASN1(key, self, callback) {
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
var p = readString(key, key._pos, self, callback);
if (p === false)
return false;
var q = readString(key, key._pos, self, callback);
if (q === false)
return false;
var g = readString(key, key._pos, self, callback);
if (g === false)
return false;
var y = readString(key, key._pos, self, callback);
if (y === false)
return false;
var asnWriter = new Ber.Writer();
asnWriter.startSequence();
// algorithm
asnWriter.startSequence();
asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa
// algorithm parameters
asnWriter.startSequence();
asnWriter.writeBuffer(p, Ber.Integer);
asnWriter.writeBuffer(q, Ber.Integer);
asnWriter.writeBuffer(g, Ber.Integer);
asnWriter.endSequence();
asnWriter.endSequence();
// subjectPublicKey
asnWriter.startSequence(Ber.BitString);
asnWriter.writeByte(0x00);
asnWriter.writeBuffer(y, Ber.Integer);
asnWriter.endSequence();
asnWriter.endSequence();
return asnWriter.buffer;
}
function ECDSAKeySSHToASN1(key, self, callback) {
// Convert SSH key parameters to ASN.1 BER values for OpenSSL
var curve = readString(key, key._pos, self, callback);
if (curve === false)
return false;
var Q = readString(key, key._pos, self, callback);
if (Q === false)
return false;
var ecCurveOID;
switch (curve.toString('ascii')) {
case 'nistp256':
// prime256v1/secp256r1
ecCurveOID = '1.2.840.10045.3.1.7';
break;
case 'nistp384':
// secp384r1
ecCurveOID = '1.3.132.0.34';
break;
case 'nistp521':
// secp521r1
ecCurveOID = '1.3.132.0.35';
break;
default:
return false;
}
var asnWriter = new Ber.Writer();
asnWriter.startSequence();
// algorithm
asnWriter.startSequence();
asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey
// algorithm parameters (namedCurve)
asnWriter.writeOID(ecCurveOID);
asnWriter.endSequence();
// subjectPublicKey
asnWriter.startSequence(Ber.BitString);
asnWriter.writeByte(0x00);
// XXX: hack to write a raw buffer without a tag -- yuck
asnWriter._ensure(Q.length);
Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length);
asnWriter._offset += Q.length;
// end hack
asnWriter.endSequence();
asnWriter.endSequence();
return asnWriter.buffer;
}
function decryptKey(keyInfo, passphrase) {
if (keyInfo._decrypted || !keyInfo.encryption)
return;
var keylen = 0;
var key;
var iv;
var dc;
keyInfo.encryption = (SSH_TO_OPENSSL[keyInfo.encryption]
|| keyInfo.encryption);
switch (keyInfo.encryption) {
case 'aes-256-cbc':
case 'aes-256-ctr':
keylen = 32;
break;
case 'des-ede3-cbc':
case 'des-ede3':
case 'aes-192-cbc':
case 'aes-192-ctr':
keylen = 24;
break;
case 'aes-128-cbc':
case 'aes-128-ctr':
case 'cast-cbc':
case 'bf-cbc':
keylen = 16;
break;
default:
throw new Error('Unsupported cipher for encrypted key: '
+ keyInfo.encryption);
}
if (keyInfo.ppk) {
iv = PPK_IV;
key = Buffer.concat([
crypto.createHash('sha1')
.update('\x00\x00\x00\x00' + passphrase, 'utf8')
.digest(),
crypto.createHash('sha1')
.update('\x00\x00\x00\x01' + passphrase, 'utf8')
.digest()
]);
key = key.slice(0, keylen);
} else {
iv = new Buffer(keyInfo.extra[0], 'hex');
key = crypto.createHash('md5')
.update(passphrase, 'utf8')
.update(iv.slice(0, 8))
.digest();
while (keylen > key.length) {
key = Buffer.concat([
key,
(crypto.createHash('md5')
.update(key)
.update(passphrase, 'utf8')
.update(iv)
.digest()).slice(0, 8)
]);
}
if (key.length > keylen)
key = key.slice(0, keylen);
}
dc = crypto.createDecipheriv(keyInfo.encryption, key, iv);
dc.setAutoPadding(false);
keyInfo.private = Buffer.concat([ dc.update(keyInfo.private), dc.final() ]);
keyInfo._decrypted = true;
if (keyInfo.privateOrig) {
// Update our original base64-encoded version of the private key
var orig = keyInfo.privateOrig.toString('utf8');
var newOrig = /^(.+(?:\r\n|\n))/.exec(orig)[1];
var b64key = keyInfo.private.toString('base64');
newOrig += b64key.match(/.{1,70}/g).join('\n');
newOrig += /((?:\r\n|\n).+)$/.exec(orig)[1];
keyInfo.privateOrig = newOrig;
} else if (keyInfo.ppk) {
var valid = verifyPPKMAC(keyInfo, passphrase, keyInfo.private);
if (!valid)
throw new Error('PPK MAC mismatch');
// Automatically convert private key data to OpenSSL format
// (including PEM)
convertPPKPrivate(keyInfo);
}
// Fill in full key type
// TODO: make DRY, we do this also in keyParser
if (keyInfo.type !== 'ec') {
keyInfo.fulltype = 'ssh-' + keyInfo.type;
} else {
// ECDSA
var asnReader = new Ber.Reader(keyInfo.private);
asnReader.readSequence();
asnReader.readInt();
asnReader.readString(Ber.OctetString, true);
asnReader.readByte(); // Skip "complex" context type byte
var offset = asnReader.readLength(); // Skip context length
if (offset !== null) {
asnReader._offset = offset;
switch (asnReader.readOID()) {
case '1.2.840.10045.3.1.7':
// prime256v1/secp256r1
keyInfo.fulltype = 'ecdsa-sha2-nistp256';
break;
case '1.3.132.0.34':
// secp384r1
keyInfo.fulltype = 'ecdsa-sha2-nistp384';
break;
case '1.3.132.0.35':
// secp521r1
keyInfo.fulltype = 'ecdsa-sha2-nistp521';
break;
}
}
if (keyInfo.fulltype === undefined)
return new Error('Unsupported EC private key type');
}
}
function genPublicKey(keyInfo) {
var publicKey;
var i;
// RSA
var n;
var e;
// DSA
var p;
var q;
var g;
var y;
// ECDSA
var d;
var Q;
var ecCurveOID;
var ecCurveName;
if (keyInfo.private) {
// parsing private key in ASN.1 format in order to generate a public key
var privKey = keyInfo.private;
var asnReader = new Ber.Reader(privKey);
var errMsg;
if (asnReader.readSequence() === null) {
errMsg = 'Malformed private key (expected sequence)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
// version (ignored)
if (asnReader.readInt() === null) {
errMsg = 'Malformed private key (expected version)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
if (keyInfo.type === 'rsa') {
// modulus (n) -- integer
n = asnReader.readString(Ber.Integer, true);
if (n === null) {
errMsg = 'Malformed private key (expected RSA n value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
// public exponent (e) -- integer
e = asnReader.readString(Ber.Integer, true);
if (e === null) {
errMsg = 'Malformed private key (expected RSA e value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
publicKey = new Buffer(4 + 7 // ssh-rsa
+ 4 + n.length
+ 4 + e.length);
publicKey.writeUInt32BE(7, 0, true);
publicKey.write('ssh-rsa', 4, 7, 'ascii');
i = 4 + 7;
publicKey.writeUInt32BE(e.length, i, true);
e.copy(publicKey, i += 4);
publicKey.writeUInt32BE(n.length, i += e.length, true);
n.copy(publicKey, i += 4);
} else if (keyInfo.type === 'dss') { // DSA
// prime (p) -- integer
p = asnReader.readString(Ber.Integer, true);
if (p === null) {
errMsg = 'Malformed private key (expected DSA p value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
// group order (q) -- integer
q = asnReader.readString(Ber.Integer, true);
if (q === null) {
errMsg = 'Malformed private key (expected DSA q value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
// group generator (g) -- integer
g = asnReader.readString(Ber.Integer, true);
if (g === null) {
errMsg = 'Malformed private key (expected DSA g value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
// public key value (y) -- integer
y = asnReader.readString(Ber.Integer, true);
if (y === null) {
errMsg = 'Malformed private key (expected DSA y value)';
if (keyInfo._decrypted)
errMsg += '. Bad passphrase?';
throw new Error(errMsg);
}
publicKey = new Buffer(4 + 7 // ssh-dss
+ 4 + p.length
+ 4 + q.length
+ 4 + g.length
+ 4 + y.length);
publicKey.writeUInt32BE(7, 0, true);
publicKey.write('ssh-dss', 4, 7, 'ascii');
i = 4 + 7;
publicKey.writeUInt32BE(p.length, i, true);
p.copy(publicKey, i += 4);
publicKey.writeUInt32BE(q.length, i += p.length, true);
q.copy(publicKey, i += 4);
publicKey.writeUInt32BE(g.length, i += q.length, true);
g.copy(publicKey, i += 4);
publicKey.writeUInt32BE(y.length, i += g.length, true);
y.copy(publicKey, i += 4);
} else { // ECDSA
d = asnReader.readString(Ber.OctetString, true);
if (d === null)
throw new Error('Malformed private key (expected ECDSA private key)');
asnReader.readByte(); // Skip "complex" context type byte
var offset = asnReader.readLength(); // Skip context length
if (offset === null)
throw new Error('Malformed private key (expected ECDSA context value)');
asnReader._offset = offset;
ecCurveOID = asnReader.readOID();
if (ecCurveOID === null)
throw new Error('Malformed private key (expected ECDSA curve)');
var tempECDH;
switch (ecCurveOID) {
case '1.2.840.10045.3.1.7':
// prime256v1/secp256r1
keyInfo.curve = ecCurveName = 'nistp256';
tempECDH = crypto.createECDH('prime256v1');
break;
case '1.3.132.0.34':
// secp384r1
keyInfo.curve = ecCurveName = 'nistp384';
tempECDH = crypto.createECDH('secp384r1');
break;
case '1.3.132.0.35':
// secp521r1
keyInfo.curve = ecCurveName = 'nistp521';
tempECDH = crypto.createECDH('secp521r1');
break;
default:
throw new Error('Malformed private key (unsupported EC curve)');
}
tempECDH.setPrivateKey(d);
Q = tempECDH.getPublicKey();
publicKey = new Buffer(4 + 19 // ecdsa-sha2-<curve name>
+ 4 + 8 // <curve name>
+ 4 + Q.length);
publicKey.writeUInt32BE(19, 0, true);
publicKey.write('ecdsa-sha2-' + ecCurveName, 4, 19, 'ascii');
publicKey.writeUInt32BE(8, 23, true);
publicKey.write(ecCurveName, 27, 8, 'ascii');
publicKey.writeUInt32BE(Q.length, 35, true);
Q.copy(publicKey, 39);
}
} else if (keyInfo.public) {
publicKey = keyInfo.public;
if (keyInfo.type === 'ec') {
// TODO: support adding ecdsa-* prefix
ecCurveName = keyInfo.curve;
} else if (publicKey[0] !== 0
// check for missing ssh-{dsa,rsa} prefix
|| publicKey[1] !== 0
|| publicKey[2] !== 0
|| publicKey[3] !== 7
|| publicKey[4] !== 115
|| publicKey[5] !== 115
|| publicKey[6] !== 104
|| publicKey[7] !== 45
|| ((publicKey[8] !== 114
|| publicKey[9] !== 115
|| publicKey[10] !== 97)
&&
((publicKey[8] !== 100
|| publicKey[9] !== 115
|| publicKey[10] !== 115)))) {
var newPK = new Buffer(4 + 7 + publicKey.length);
publicKey.copy(newPK, 11);
newPK.writeUInt32BE(7, 0, true);
if (keyInfo.type === 'rsa')
newPK.write('ssh-rsa', 4, 7, 'ascii');
else
newPK.write('ssh-dss', 4, 7, 'ascii');
publicKey = newPK;
}
} else
throw new Error('Missing data generated by parseKey()');
// generate a public key format for use with OpenSSL
i = 4 + 7;
var fulltype;
var asn1KeyBuf;
if (keyInfo.type === 'rsa') {
fulltype = 'ssh-rsa';
asn1KeyBuf = RSAKeySSHToASN1(publicKey.slice(4 + 7));
} else if (keyInfo.type === 'dss') {
fulltype = 'ssh-dss';
asn1KeyBuf = DSAKeySSHToASN1(publicKey.slice(4 + 7));
} else { // ECDSA
fulltype = 'ecdsa-sha2-' + ecCurveName;
asn1KeyBuf = ECDSAKeySSHToASN1(publicKey.slice(4 + 19));
}
if (!asn1KeyBuf)
throw new Error('Invalid SSH-formatted public key');
var b64key = asn1KeyBuf.toString('base64').replace(RE_KEY_LEN, '$1\n');
var fullkey = '-----BEGIN PUBLIC KEY-----\n'
+ b64key
+ (b64key[b64key.length - 1] === '\n' ? '' : '\n')
+ '-----END PUBLIC KEY-----';
return {
type: keyInfo.type,
fulltype: fulltype,
curve: ecCurveName,
public: publicKey,
publicOrig: new Buffer(fullkey)
};
}
function verifyPPKMAC(keyInfo, passphrase, privateKey) {
if (keyInfo._macresult !== undefined)
return keyInfo._macresult;
else if (!keyInfo.ppk)
throw new Error("Key isn't a PPK");
else if (!keyInfo.privateMAC)
throw new Error('Missing MAC');
else if (!privateKey)
throw new Error('Missing raw private key data');
else if (keyInfo.encryption && typeof passphrase !== 'string')
throw new Error('Missing passphrase for encrypted PPK');
else if (keyInfo.encryption && !keyInfo._decrypted)
throw new Error('PPK must be decrypted before verifying MAC');
var mac = keyInfo.privateMAC;
var typelen = keyInfo.fulltype.length;
// encryption algorithm is converted at this point for use with OpenSSL,
// so we need to use the original value so that the MAC is calculated
// correctly
var enc = (keyInfo.encryption ? 'aes256-cbc' : 'none');
var enclen = enc.length;
var commlen = Buffer.byteLength(keyInfo.comment);
var pub = keyInfo.public;
var publen = pub.length;
var privlen = privateKey.length;
var macdata = new Buffer(4 + typelen
+ 4 + enclen
+ 4 + commlen
+ 4 + publen
+ 4 + privlen);
var p = 0;
macdata.writeUInt32BE(typelen, p, true);
macdata.write(keyInfo.fulltype, p += 4, typelen, 'ascii');
macdata.writeUInt32BE(enclen, p += typelen, true);
macdata.write(enc, p += 4, enclen, 'ascii');
macdata.writeUInt32BE(commlen, p += enclen, true);
macdata.write(keyInfo.comment, p += 4, commlen, 'utf8');
macdata.writeUInt32BE(publen, p += commlen, true);
pub.copy(macdata, p += 4);
macdata.writeUInt32BE(privlen, p += publen, true);
privateKey.copy(macdata, p += 4);
if (typeof passphrase !== 'string')
passphrase = '';
var mackey = crypto.createHash('sha1')
.update('putty-private-key-file-mac-key', 'ascii')
.update(passphrase, 'utf8')
.digest();
var calcMAC = crypto.createHmac('sha1', mackey)
.update(macdata)
.digest('hex');
return (keyInfo._macresult = (calcMAC === mac));
}
function convertPPKPrivate(keyInfo) {
if (!keyInfo.ppk || !keyInfo.public || !keyInfo.private)
throw new Error("Key isn't a PPK");
else if (keyInfo._converted)
return false;
var pub = keyInfo.public;
var priv = keyInfo.private;
var asnWriter = new Ber.Writer();
var p;
var q;
if (keyInfo.type === 'rsa') {
var e = readString(pub, 4 + 7);
var n = readString(pub, pub._pos);
var d = readString(priv, 0);
p = readString(priv, priv._pos);
q = readString(priv, priv._pos);
var iqmp = readString(priv, priv._pos);
var p1 = new BigInteger(p, 256);
var q1 = new BigInteger(q, 256);
var dmp1 = new BigInteger(d, 256);
var dmq1 = new BigInteger(d, 256);
dmp1 = new Buffer(dmp1.mod(p1.subtract(BigInteger.ONE)).toByteArray());
dmq1 = new Buffer(dmq1.mod(q1.subtract(BigInteger.ONE)).toByteArray());
asnWriter.startSequence();
asnWriter.writeInt(0x00, Ber.Integer);
asnWriter.writeBuffer(n, Ber.Integer);
asnWriter.writeBuffer(e, Ber.Integer);
asnWriter.writeBuffer(d, Ber.Integer);
asnWriter.writeBuffer(p, Ber.Integer);
asnWriter.writeBuffer(q, Ber.Integer);
asnWriter.writeBuffer(dmp1, Ber.Integer);
asnWriter.writeBuffer(dmq1, Ber.Integer);
asnWriter.writeBuffer(iqmp, Ber.Integer);
asnWriter.endSequence();
} else {
p = readString(pub, 4 + 7);
q = readString(pub, pub._pos);
var g = readString(pub, pub._pos);
var y = readString(pub, pub._pos);
var x = readString(priv, 0);
asnWriter.startSequence();
asnWriter.writeInt(0x00, Ber.Integer);
asnWriter.writeBuffer(p, Ber.Integer);
asnWriter.writeBuffer(q, Ber.Integer);
asnWriter.writeBuffer(g, Ber.Integer);
asnWriter.writeBuffer(y, Ber.Integer);
asnWriter.writeBuffer(x, Ber.Integer);
asnWriter.endSequence();
}
var b64key = asnWriter.buffer.toString('base64').replace(RE_KEY_LEN, '$1\n');
var fullkey = '-----BEGIN '
+ (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
+ ' PRIVATE KEY-----\n'
+ b64key
+ (b64key[b64key.length - 1] === '\n' ? '' : '\n')
+ '-----END '
+ (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
+ ' PRIVATE KEY-----';
keyInfo.private = asnWriter.buffer;
keyInfo.privateOrig = new Buffer(fullkey);
keyInfo._converted = true;
return true;
}
function readString(buffer, start, encoding, stream, cb, maxLen) {
if (encoding && !Buffer.isBuffer(encoding) && typeof encoding !== 'string') {
if (typeof cb === 'number')
maxLen = cb;
cb = stream;
stream = encoding;
encoding = undefined;
}
start || (start = 0);
var bufferLen = buffer.length;
var left = (bufferLen - start);
var len;
var end;
if (start < 0 || start >= bufferLen || left < 4) {
stream && stream._cleanup(cb);
return false;
}
len = buffer.readUInt32BE(start, true);
if (len > (maxLen || MAX_STRING_LEN) || left < (4 + len)) {
stream && stream._cleanup(cb);
return false;
}
start += 4;
end = start + len;
buffer._pos = end;
if (encoding) {
if (Buffer.isBuffer(encoding)) {
buffer.copy(encoding, 0, start, end);
return encoding;
} else
return buffer.toString(encoding, start, end);
} else
return buffer.slice(start, end);
}