a squeaky clean start
This commit is contained in:
parent
5917757dfb
commit
8d2ebe77fe
7 changed files with 196 additions and 80 deletions
89
bin/rasha.js
89
bin/rasha.js
|
@ -1,76 +1,33 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var Rasha = require('../index.js');
|
||||||
|
|
||||||
var infile = process.argv[2];
|
var infile = process.argv[2];
|
||||||
|
var format = process.argv[3];
|
||||||
|
|
||||||
var pem = fs.readFileSync(infile, 'ascii');
|
var key = fs.readFileSync(infile, 'ascii');
|
||||||
var b64 = pem.split(/\n/).filter(function (line) {
|
|
||||||
// TODO test if RSA key
|
|
||||||
if (/^---/.test(line)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).join('');
|
|
||||||
var buf = Buffer.from(b64, 'base64');
|
|
||||||
|
|
||||||
var ELOOP = "uASN1.js Error: iterated over 100+ elements (probably a malformed file)";
|
try {
|
||||||
var EDEEP = "uASN1.js Error: element nested 100+ layers deep (probably a malformed file)";
|
key = JSON.parse(key);
|
||||||
var ASN1 = require('../lib/uasn1.js');
|
} catch(e) {
|
||||||
/*
|
// ignore
|
||||||
function ASN1(buf, depth) {
|
|
||||||
if (depth >= 100) {
|
|
||||||
throw new Error(EDEEP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// start after type (0) and lengthSize (1)
|
if ('string' === typeof key) {
|
||||||
var index = 2;
|
var pub = (-1 !== [ 'public', 'spki', 'pkix' ].indexOf(format));
|
||||||
var asn1 = {
|
Rasha.import({ pem: key, public: (pub || format) }).then(function (jwk) {
|
||||||
type: buf[0]
|
console.log(JSON.stringify(jwk, null, 2));
|
||||||
, lengthSize: 0
|
}).catch(function (err) {
|
||||||
, length: buf[1]
|
console.error(err);
|
||||||
};
|
process.exit(1);
|
||||||
var child;
|
});
|
||||||
var i = 0;
|
} else {
|
||||||
if (0x80 & asn1.length) {
|
Rasha.export({ jwk: key, format: format }).then(function (pem) {
|
||||||
asn1.lengthSize = 0x7f & asn1.length;
|
console.log(pem);
|
||||||
// I think that buf->hex->int solves the problem of Endianness... not sure
|
}).catch(function (err) {
|
||||||
asn1.length = parseInt(buf.slice(index, index + asn1.lengthSize).toString('hex'), 16);
|
console.error(err);
|
||||||
// add back the original byte indicating lengthSize
|
process.exit(2);
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a primitive value type
|
|
||||||
if (asn1.type <= 0x06) {
|
|
||||||
i += 1;
|
|
||||||
asn1.value = buf.slice(index, index + asn1.length);
|
|
||||||
return asn1;
|
|
||||||
}
|
|
||||||
|
|
||||||
asn1.children = [];
|
|
||||||
while (i < 100 && index < buf.byteLength) {
|
|
||||||
child = ASN1(buf.slice(index), (depth || 0) + 1);
|
|
||||||
index += (2 + child.lengthSize + child.length);
|
|
||||||
asn1.children.push(child);
|
|
||||||
}
|
|
||||||
if (i >= 100) { throw new Error(ELOOP); }
|
|
||||||
|
|
||||||
return asn1;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
var asn1 = ASN1.parse(buf);
|
|
||||||
var ws = '';
|
|
||||||
function write(asn1) {
|
|
||||||
console.log(ws, 'ch', Buffer.from([asn1.type]).toString('hex'), asn1.length);
|
|
||||||
if (!asn1.children) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
asn1.children.forEach(function (a, i) {
|
|
||||||
ws += '\t';
|
|
||||||
write(a);
|
|
||||||
ws = ws.slice(1);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log(JSON.stringify(asn1, null, 2));
|
|
||||||
//console.log(asn1);
|
|
||||||
write(asn1);
|
|
||||||
|
|
2
index.js
Normal file
2
index.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
'use strict';
|
||||||
|
module.exports = require('./lib/rasha.js');
|
|
@ -1,18 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ELOOP = "uASN1.js Error: iterated over 100+ elements (probably a malformed file)";
|
|
||||||
var EDEEP = "uASN1.js Error: element nested 100+ layers deep (probably a malformed file)";
|
|
||||||
// Container Types are Sequence 0x30, Octect String 0x04, Array? (0xA0, 0xA1)
|
|
||||||
// Value Types are Integer 0x02, Bit String 0x03, Null 0x05, Object ID 0x06,
|
|
||||||
// Sometimes Bit String is used as a container (RSA Pub Spki)
|
|
||||||
var VTYPES = [ 0x02, 0x03, 0x05, 0x06 ];
|
|
||||||
|
|
||||||
var ASN1 = module.exports = function ASN1() {
|
var ASN1 = module.exports = function ASN1() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ASN1.ELOOP = "uASN1.js Error: iterated over 15+ elements (probably a malformed file)";
|
||||||
|
ASN1.EDEEP = "uASN1.js Error: element nested 10+ layers deep (probably a malformed file)";
|
||||||
|
// Container Types are Sequence 0x30, Octect String 0x04, Array? (0xA0, 0xA1)
|
||||||
|
// Value Types are Integer 0x02, Bit String 0x03, Null 0x05, Object ID 0x06,
|
||||||
|
// Sometimes Bit String is used as a container (RSA Pub Spki)
|
||||||
|
ASN1.VTYPES = [ 0x02, 0x03, 0x05, 0x06 ];
|
||||||
ASN1.parse = function parseAsn1(buf, depth) {
|
ASN1.parse = function parseAsn1(buf, depth) {
|
||||||
console.log('');
|
if (depth >= 10) { throw new Error(ASN1.EDEEP); }
|
||||||
if (depth >= 100) { throw new Error(EDEEP); }
|
|
||||||
|
|
||||||
var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
|
var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
|
||||||
var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
|
var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
|
||||||
|
@ -30,27 +28,25 @@ ASN1.parse = function parseAsn1(buf, depth) {
|
||||||
|
|
||||||
// High-order bit Integers have a leading 0x00 to signify that they are positive.
|
// High-order bit Integers have a leading 0x00 to signify that they are positive.
|
||||||
// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
|
// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
|
||||||
console.log(buf[index], asn1.type);
|
|
||||||
if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
|
if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
|
||||||
console.log('chomp');
|
|
||||||
index += 1;
|
index += 1;
|
||||||
adjust = -1;
|
adjust = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a primitive value type
|
// this is a primitive value type
|
||||||
if (-1 !== VTYPES.indexOf(asn1.type)) {
|
if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) {
|
||||||
asn1.value = buf.slice(index, index + asn1.length + adjust);
|
asn1.value = buf.slice(index, index + asn1.length + adjust);
|
||||||
return asn1;
|
return asn1;
|
||||||
}
|
}
|
||||||
|
|
||||||
asn1.children = [];
|
asn1.children = [];
|
||||||
while (iters < 100 && index < buf.byteLength) {
|
while (iters < 15 && index < buf.byteLength) {
|
||||||
iters += 1;
|
iters += 1;
|
||||||
child = ASN1.parse(buf.slice(index, index + asn1.length), (depth || 0) + 1);
|
child = ASN1.parse(buf.slice(index, index + asn1.length), (depth || 0) + 1);
|
||||||
index += (2 + child.lengthSize + child.length);
|
index += (2 + child.lengthSize + child.length);
|
||||||
asn1.children.push(child);
|
asn1.children.push(child);
|
||||||
}
|
}
|
||||||
if (iters >= 100) { throw new Error(ELOOP); }
|
if (iters >= 15) { throw new Error(ASN1.ELOOP); }
|
||||||
|
|
||||||
return asn1;
|
return asn1;
|
||||||
};
|
};
|
56
lib/encoding.js
Normal file
56
lib/encoding.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Enc = module.exports;
|
||||||
|
|
||||||
|
Enc.bufToHex = function toHex(u8) {
|
||||||
|
var hex = [];
|
||||||
|
var i, h;
|
||||||
|
|
||||||
|
for (i = 0; i < u8.byteLength; i += 1) {
|
||||||
|
h = u8[i].toString(16);
|
||||||
|
if (2 !== h.length) { h = '0' + h; }
|
||||||
|
hex.push(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.join('').toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enc.strToBin = function strToBin(str) {
|
||||||
|
var escstr = encodeURIComponent(str);
|
||||||
|
// replaces any uri escape sequence, such as %0A,
|
||||||
|
// with binary escape, such as 0x0A
|
||||||
|
var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
||||||
|
return String.fromCharCode(parseInt(p1, 16));
|
||||||
|
});
|
||||||
|
|
||||||
|
return binstr;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enc.strToBase64 = function strToBase64(str) {
|
||||||
|
// node automatically can tell the difference
|
||||||
|
// between uc2 (utf-8) strings and binary strings
|
||||||
|
// so we don't have to re-encode the strings
|
||||||
|
return Buffer.from(str).toString('base64');
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
|
||||||
|
var r = str % 4;
|
||||||
|
if (2 === r) {
|
||||||
|
str += '==';
|
||||||
|
} else if (3 === r) {
|
||||||
|
str += '=';
|
||||||
|
}
|
||||||
|
return str.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
Enc.base64ToBuf = function base64ToBuf(str) {
|
||||||
|
// always convert from urlsafe base64, just in case
|
||||||
|
//return Buffer.from(Enc.urlBase64ToBase64(str)).toString('base64');
|
||||||
|
return Buffer.from(str, 'base64');
|
||||||
|
};
|
39
lib/pem.js
Normal file
39
lib/pem.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PEM = module.exports;
|
||||||
|
var Enc = require('./encoding.js');
|
||||||
|
|
||||||
|
PEM.RSA_OBJID = '06 09 2A864886F70D010101'
|
||||||
|
.replace(/\s+/g, '').toLowerCase();
|
||||||
|
|
||||||
|
PEM.parseBlock = function pemToDer(pem) {
|
||||||
|
var typ;
|
||||||
|
var pub;
|
||||||
|
var hex;
|
||||||
|
var der = Enc.base64ToBuf(pem.split(/\n/).filter(function (line, i) {
|
||||||
|
if (0 === i) {
|
||||||
|
if (/ PUBLIC /.test(line)) {
|
||||||
|
pub = true;
|
||||||
|
} else if (/ PRIVATE /.test(line)) {
|
||||||
|
pub = false;
|
||||||
|
}
|
||||||
|
if (/ RSA /.test(line)) {
|
||||||
|
typ = 'RSA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !/---/.test(line);
|
||||||
|
}).join(''));
|
||||||
|
|
||||||
|
if (!typ) {
|
||||||
|
hex = Enc.bufToHex(der);
|
||||||
|
if (-1 !== hex.indexOf(PEM.RSA_OBJID)) {
|
||||||
|
typ = 'RSA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!typ) {
|
||||||
|
console.warn("Definitely not an RSA PKCS#8 because there's no RSA Object ID in the DER body.");
|
||||||
|
console.warn("Probably not an RSA PKCS#1 because 'RSA' wasn't in the PEM type string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { kty: typ, pub: pub, der: der };
|
||||||
|
};
|
56
lib/rasha.js
56
lib/rasha.js
|
@ -1,3 +1,59 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RSA = module.exports;
|
var RSA = module.exports;
|
||||||
|
var ASN1 = require('./asn1.js');
|
||||||
|
//var Enc = require('./encoding.js');
|
||||||
|
var PEM = require('./pem.js');
|
||||||
|
var SSH = require('./ssh.js');
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
RSAPrivateKey ::= SEQUENCE {
|
||||||
|
version Version,
|
||||||
|
modulus INTEGER, -- n
|
||||||
|
publicExponent INTEGER, -- e
|
||||||
|
privateExponent INTEGER, -- d
|
||||||
|
prime1 INTEGER, -- p
|
||||||
|
prime2 INTEGER, -- q
|
||||||
|
exponent1 INTEGER, -- d mod (p-1)
|
||||||
|
exponent2 INTEGER, -- d mod (q-1)
|
||||||
|
coefficient INTEGER, -- (inverse of q) mod p
|
||||||
|
otherPrimeInfos OtherPrimeInfos OPTIONAL
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global Promise*/
|
||||||
|
RSA.parse = function parseEc(opts) {
|
||||||
|
return Promise.resolve().then(function () {
|
||||||
|
if (!opts || !opts.pem || 'string' !== typeof opts.pem) {
|
||||||
|
throw new Error("must pass { pem: pem } as a string");
|
||||||
|
}
|
||||||
|
if (0 === opts.pem.indexOf('ssh-rsa ')) {
|
||||||
|
return SSH.parse(opts.pem);
|
||||||
|
}
|
||||||
|
var pem = opts.pem;
|
||||||
|
var block = PEM.parseBlock(pem);
|
||||||
|
//var hex = toHex(u8);
|
||||||
|
//var jwk = { kty: 'RSA' };
|
||||||
|
|
||||||
|
var asn1 = ASN1.parse(block.der);
|
||||||
|
var ws = '';
|
||||||
|
function write(asn1) {
|
||||||
|
console.log(ws, 'ch', Buffer.from([asn1.type]).toString('hex'), asn1.length);
|
||||||
|
if (!asn1.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
asn1.children.forEach(function (a) {
|
||||||
|
ws += '\t';
|
||||||
|
write(a);
|
||||||
|
ws = ws.slice(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//console.log(JSON.stringify(asn1, null, 2));
|
||||||
|
console.log(asn1);
|
||||||
|
write(asn1);
|
||||||
|
|
||||||
|
return { kty: 'RSA' };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
RSA.toJwk = RSA.import = RSA.parse;
|
||||||
|
|
10
lib/ssh.js
Normal file
10
lib/ssh.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var SSH = module.exports;
|
||||||
|
|
||||||
|
// 7 s s h - r s a
|
||||||
|
SSH.RSA = '00000007 73 73 68 2d 72 73 61'.replace(/\s+/g, '').toLowerCase();
|
||||||
|
|
||||||
|
SSH.parse = function (pem) {
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in a new issue