Skip to content

Commit ef7ed7b

Browse files
committed
new exporting key api
1 parent 080df0a commit ef7ed7b

File tree

15 files changed

+269
-226
lines changed

15 files changed

+269
-226
lines changed

src/NodeRSA.js

Lines changed: 30 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,26 @@ module.exports = (function () {
2626
var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1_oaep';
2727
var DEFAULT_SIGNING_SCHEME = 'pkcs1';
2828

29-
var DEFAULT_EXPORT_PRIVATE_FORMAT = 'pkcs1';
30-
var DEFAULT_EXPORT_PUBLIC_FORMAT = 'pkcs8';
29+
var DEFAULT_EXPORT_FORMAT = 'private';
30+
var EXPORT_FORMAT_ALIASES = {
31+
'private': 'pkcs1-private-pem',
32+
'public': 'pkcs8-public-pem'
33+
};
3134

3235
/**
3336
* @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent}
3437
* @constructor
3538
*/
36-
function NodeRSA(key, options) {
39+
function NodeRSA(key, format, options) {
3740
if (!this instanceof NodeRSA) {
3841
return new NodeRSA(key, options);
3942
}
4043

44+
if (_.isObject(format)) {
45+
options = format;
46+
format = undefined;
47+
}
48+
4149
this.$options = {
4250
signingScheme: DEFAULT_SIGNING_SCHEME,
4351
signingSchemeOptions: {
@@ -53,14 +61,14 @@ module.exports = (function () {
5361
rsaUtils: this
5462
};
5563
this.keyPair = new rsa.Key();
56-
this.setOptions(options);
5764
this.$cache = {};
5865

5966
if (Buffer.isBuffer(key) || _.isString(key)) {
60-
this.importKey(key);
67+
this.importKey(key, format);
6168
} else if (_.isObject(key)) {
6269
this.generateKeyPair(key.b, key.e);
6370
}
71+
this.setOptions(options);
6472
}
6573

6674
/**
@@ -153,81 +161,46 @@ module.exports = (function () {
153161
/**
154162
* Importing key
155163
* @param keyData {string|buffer}
164+
* @param format {string}
156165
*/
157166
NodeRSA.prototype.importKey = function (keyData, format) {
158-
if(format === undefined && !formats.detectAndImport(this.keyPair, keyData, format)) {
167+
if (!keyData) {
168+
throw Error("Empty key given");
169+
}
170+
171+
if(!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) {
159172
throw Error("Key format must be specified");
160173
}
161174
this.$cache = {};
162175
};
163176

164177
/**
165-
* Make key form private PEM string
166-
*
167-
* @param privatePEM {string}
178+
* Exporting key
179+
* @param format {string}
168180
*/
169-
/*NodeRSA.prototype.$loadFromPrivatePEM = function (privatePEM, encoding) {
170-
var pem = privatePEM
171-
.replace('-----BEGIN RSA PRIVATE KEY-----', '')
172-
.replace('-----END RSA PRIVATE KEY-----', '')
173-
.replace(/\s+|\n\r|\n|\r$/gm, '');
174-
var reader = new ber.Reader(new Buffer(pem, 'base64'));
175-
176-
reader.readSequence();
177-
reader.readString(2, true); // just zero
178-
this.keyPair.setPrivate(
179-
reader.readString(2, true), // modulus
180-
reader.readString(2, true), // publicExponent
181-
reader.readString(2, true), // privateExponent
182-
reader.readString(2, true), // prime1
183-
reader.readString(2, true), // prime2
184-
reader.readString(2, true), // exponent1 -- d mod (p1)
185-
reader.readString(2, true), // exponent2 -- d mod (q-1)
186-
reader.readString(2, true) // coefficient -- (inverse of q) mod p
187-
);
188-
189-
};*/
181+
NodeRSA.prototype.exportKey = function (format) {
182+
format = format || DEFAULT_EXPORT_FORMAT;
183+
format = EXPORT_FORMAT_ALIASES[format] || format;
190184

191-
/**
192-
* Make key form public PEM string
193-
*
194-
* @param publicPEM {string}
195-
*/
196-
/*NodeRSA.prototype.$loadFromPublicPEM = function (publicPEM, encoding) {
197-
var pem = publicPEM
198-
.replace('-----BEGIN PUBLIC KEY-----', '')
199-
.replace('-----END PUBLIC KEY-----', '')
200-
.replace(/\s+|\n\r|\n|\r$/gm, '');
201-
var reader = new ber.Reader(new Buffer(pem, 'base64'));
202-
203-
reader.readSequence();
204-
var header = new ber.Reader(reader.readString(0x30, true));
205-
if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
206-
throw Error('Invalid Public key PEM format');
185+
if (!this.$cache[format]) {
186+
this.$cache[format] = formats.detectAndExport(this.keyPair, format);
207187
}
208-
209-
var body = new ber.Reader(reader.readString(0x03, true));
210-
body.readByte();
211-
body.readSequence();
212-
this.keyPair.setPublic(
213-
body.readString(0x02, true), // modulus
214-
body.readString(0x02, true) // publicExponent
215-
);
216-
};*/
188+
return this.$cache[format];
189+
};
217190

218191
/**
219192
* Check if key pair contains private key
220193
*/
221194
NodeRSA.prototype.isPrivate = function () {
222-
return this.keyPair.n && this.keyPair.e && this.keyPair.d || false;
195+
return this.keyPair.isPrivate();
223196
};
224197

225198
/**
226199
* Check if key pair contains public key
227200
* @param strict {boolean} - public key only, return false if have private exponent
228201
*/
229202
NodeRSA.prototype.isPublic = function (strict) {
230-
return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d) || false;
203+
return this.keyPair.isPublic(strict);
231204
};
232205

233206
/**
@@ -316,56 +289,6 @@ module.exports = (function () {
316289
return this.keyPair.verify(this.$getDataForEncrypt(buffer, source_encoding), signature, signature_encoding);
317290
};
318291

319-
/**
320-
* Exporting private key
321-
*
322-
* @param format
323-
* @returns {*}
324-
*/
325-
NodeRSA.prototype.exportPrivate = function (format) {
326-
if (!this.isPrivate()) {
327-
throw Error("It is not private key");
328-
}
329-
330-
format = format || DEFAULT_EXPORT_PRIVATE_FORMAT;
331-
if (this.$cache.privateKey && this.$cache.privateKey[format]) {
332-
return this.$cache.privateKey[format];
333-
} else {
334-
var fmt = format.split('-');
335-
if (!formats.isPrivateExport(fmt[0])) {
336-
throw Error('Unsupported private key export format');
337-
}
338-
339-
this.$cache.privateKey = this.$cache.privateKey || {};
340-
return this.$cache.privateKey[format] = formats[fmt[0]].privateExport(this.keyPair, fmt[1]);
341-
}
342-
};
343-
344-
/**
345-
* Exporting public key
346-
*
347-
* @param format
348-
* @returns {*}
349-
*/
350-
NodeRSA.prototype.exportPublic = function (format) {
351-
if (!this.isPublic()) {
352-
throw Error("It is not public key");
353-
}
354-
355-
format = format || DEFAULT_EXPORT_PUBLIC_FORMAT;
356-
if (this.$cache.publicKey && this.$cache.publicKey[format]) {
357-
return this.$cache.publicKey[format];
358-
} else {
359-
var fmt = format.split('-');
360-
if (!formats.isPublicExport(fmt[0])) {
361-
throw Error('Unsupported public key export format');
362-
}
363-
364-
this.$cache.publicKey = this.$cache.publicKey || {};
365-
return this.$cache.publicKey[format] = formats[fmt[0]].publicExport(this.keyPair, fmt[1]);
366-
}
367-
};
368-
369292
/**
370293
* Returns key size in bits
371294
* @returns {int}
@@ -419,51 +342,5 @@ module.exports = (function () {
419342
}
420343
};
421344

422-
/**
423-
* private
424-
* Recalculating properties
425-
*/
426-
/*NodeRSA.prototype.$recalculateCache = function () {
427-
this.$cache.privatePEM = this.$makePrivatePEM();
428-
};*/
429-
430-
/**
431-
* private
432-
* @returns {string} private PEM string
433-
*/
434-
/*NodeRSA.prototype.$makePrivatePEM = function () {
435-
if (!this.isPrivate()) {
436-
return null;
437-
}
438-
439-
var n = this.keyPair.n.toBuffer();
440-
var d = this.keyPair.d.toBuffer();
441-
var p = this.keyPair.p.toBuffer();
442-
var q = this.keyPair.q.toBuffer();
443-
var dmp1 = this.keyPair.dmp1.toBuffer();
444-
var dmq1 = this.keyPair.dmq1.toBuffer();
445-
var coeff = this.keyPair.coeff.toBuffer();
446-
447-
var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
448-
var writer = new ber.Writer({size: length});
449-
450-
writer.startSequence();
451-
writer.writeInt(0);
452-
writer.writeBuffer(n, 2);
453-
writer.writeInt(this.keyPair.e);
454-
writer.writeBuffer(d, 2);
455-
writer.writeBuffer(p, 2);
456-
writer.writeBuffer(q, 2);
457-
writer.writeBuffer(dmp1, 2);
458-
writer.writeBuffer(dmq1, 2);
459-
writer.writeBuffer(coeff, 2);
460-
writer.endSequence();
461-
462-
return '-----BEGIN RSA PRIVATE KEY-----\n' +
463-
utils.linebrk(writer.buffer.toString('base64'), 64) +
464-
'\n-----END RSA PRIVATE KEY-----';
465-
};
466-
*/
467-
468345
return NodeRSA;
469346
})();

src/formats/formats.js

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,36 @@ module.exports = {
2020
},
2121

2222
detectAndImport: function (key, data, format) {
23-
if (format === undefined && _.isString(data)) {
23+
if (format === undefined) {
2424
for (var format in module.exports) {
2525
if (typeof module.exports[format].autoImport === 'function' && module.exports[format].autoImport(key, data)) {
2626
return true;
2727
}
2828
}
2929
} else if (format) {
3030
var fmt = format.split('-');
31-
var keyType = fmt[1] === 'private' || fmt[1] === 'public' ? fmt[1] : 'private';
32-
var keyOpt = fmt[2] === 'der' ? {binary: true} : null;
31+
var keyType = 'private';
32+
var keyOpt = {type: 'default'};
33+
34+
for(var i = 1; i < fmt.length; i++) {
35+
if (fmt[i]) {
36+
switch (fmt[i]) {
37+
case 'public':
38+
keyType = fmt[i];
39+
break;
40+
case 'private':
41+
keyType = fmt[i];
42+
break;
43+
case 'pem':
44+
keyOpt.type = fmt[i];
45+
break;
46+
case 'der':
47+
keyOpt.type = fmt[i];
48+
break;
49+
}
50+
}
51+
}
52+
3353
if (module.exports[fmt[0]]) {
3454
if (keyType === 'private') {
3555
module.exports[fmt[0]].privateImport(key, data, keyOpt);
@@ -42,5 +62,48 @@ module.exports = {
4262
}
4363

4464
return false;
65+
},
66+
67+
detectAndExport: function(key, format) {
68+
if (format) {
69+
var fmt = format.split('-');
70+
var keyType = 'private';
71+
var keyOpt = {type: 'default'};
72+
73+
for(var i = 1; i < fmt.length; i++) {
74+
if (fmt[i]) {
75+
switch (fmt[i]) {
76+
case 'public':
77+
keyType = fmt[i];
78+
break;
79+
case 'private':
80+
keyType = fmt[i];
81+
break;
82+
case 'pem':
83+
keyOpt.type = fmt[i];
84+
break;
85+
case 'der':
86+
keyOpt.type = fmt[i];
87+
break;
88+
}
89+
}
90+
}
91+
92+
if (module.exports[fmt[0]]) {
93+
if (keyType === 'private') {
94+
if (!key.isPrivate()) {
95+
throw Error("It is not private key");
96+
}
97+
return module.exports[fmt[0]].privateExport(key, keyOpt);
98+
} else {
99+
if (!key.isPublic()) {
100+
throw Error("It is not public key");
101+
}
102+
return module.exports[fmt[0]].publicExport(key, keyOpt);
103+
}
104+
} else {
105+
throw Error('Unsupported key format');
106+
}
107+
}
45108
}
46109
};

src/formats/pkcs1.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,30 @@ module.exports = {
2929
writer.writeBuffer(coeff, 2);
3030
writer.endSequence();
3131

32-
if (options.binary) {
32+
if (options.type === 'der') {
3333
return writer.buffer;
3434
} else {
3535
return '-----BEGIN RSA PRIVATE KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END RSA PRIVATE KEY-----';
3636
}
3737
},
3838

39-
privateImport: function (key, data) {
39+
privateImport: function (key, data, options) {
40+
options = options || {};
4041
var buffer;
4142

42-
if (_.isString(data)) {
43-
var pem = data.replace('-----BEGIN RSA PRIVATE KEY-----', '')
44-
.replace('-----END RSA PRIVATE KEY-----', '')
45-
.replace(/\s+|\n\r|\n|\r$/gm, '');
46-
buffer = new Buffer(pem, 'base64');
43+
if (options.type !== 'der') {
44+
if (Buffer.isBuffer(data)) {
45+
data = data.toString('utf8');
46+
}
47+
48+
if (_.isString(data)) {
49+
var pem = data.replace('-----BEGIN RSA PRIVATE KEY-----', '')
50+
.replace('-----END RSA PRIVATE KEY-----', '')
51+
.replace(/\s+|\n\r|\n|\r$/gm, '');
52+
buffer = new Buffer(pem, 'base64');
53+
} else {
54+
throw Error('Unsupported key format');
55+
}
4756
} else if (Buffer.isBuffer(data)) {
4857
buffer = data;
4958
} else {

0 commit comments

Comments
 (0)