Skip to content

Commit 080df0a

Browse files
committed
auto detect key type for import
1 parent 5679171 commit 080df0a

File tree

5 files changed

+180
-69
lines changed

5 files changed

+180
-69
lines changed

src/NodeRSA.js

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -151,21 +151,13 @@ module.exports = (function () {
151151
};
152152

153153
/**
154-
* Load key from PEM string
155-
* @param pem {string}
154+
* Importing key
155+
* @param keyData {string|buffer}
156156
*/
157-
NodeRSA.prototype.importKey = function (pem) {
158-
if (Buffer.isBuffer(pem)) {
159-
pem = pem.toString('utf8');
157+
NodeRSA.prototype.importKey = function (keyData, format) {
158+
if(format === undefined && !formats.detectAndImport(this.keyPair, keyData, format)) {
159+
throw Error("Key format must be specified");
160160
}
161-
162-
if (/^\s*-----BEGIN RSA PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PRIVATE KEY-----\s*$/g.test(pem)) {
163-
this.$loadFromPrivatePEM(pem, 'base64');
164-
} else if (/^\s*-----BEGIN PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PUBLIC KEY-----\s*$/g.test(pem)) {
165-
this.$loadFromPublicPEM(pem, 'base64');
166-
} else
167-
throw Error('Invalid PEM format');
168-
169161
this.$cache = {};
170162
};
171163

@@ -174,7 +166,7 @@ module.exports = (function () {
174166
*
175167
* @param privatePEM {string}
176168
*/
177-
NodeRSA.prototype.$loadFromPrivatePEM = function (privatePEM, encoding) {
169+
/*NodeRSA.prototype.$loadFromPrivatePEM = function (privatePEM, encoding) {
178170
var pem = privatePEM
179171
.replace('-----BEGIN RSA PRIVATE KEY-----', '')
180172
.replace('-----END RSA PRIVATE KEY-----', '')
@@ -194,14 +186,14 @@ module.exports = (function () {
194186
reader.readString(2, true) // coefficient -- (inverse of q) mod p
195187
);
196188
197-
};
189+
};*/
198190

199191
/**
200192
* Make key form public PEM string
201193
*
202194
* @param publicPEM {string}
203195
*/
204-
NodeRSA.prototype.$loadFromPublicPEM = function (publicPEM, encoding) {
196+
/*NodeRSA.prototype.$loadFromPublicPEM = function (publicPEM, encoding) {
205197
var pem = publicPEM
206198
.replace('-----BEGIN PUBLIC KEY-----', '')
207199
.replace('-----END PUBLIC KEY-----', '')
@@ -221,7 +213,7 @@ module.exports = (function () {
221213
body.readString(0x02, true), // modulus
222214
body.readString(0x02, true) // publicExponent
223215
);
224-
};
216+
};*/
225217

226218
/**
227219
* Check if key pair contains private key
@@ -324,6 +316,12 @@ module.exports = (function () {
324316
return this.keyPair.verify(this.$getDataForEncrypt(buffer, source_encoding), signature, signature_encoding);
325317
};
326318

319+
/**
320+
* Exporting private key
321+
*
322+
* @param format
323+
* @returns {*}
324+
*/
327325
NodeRSA.prototype.exportPrivate = function (format) {
328326
if (!this.isPrivate()) {
329327
throw Error("It is not private key");
@@ -343,6 +341,12 @@ module.exports = (function () {
343341
}
344342
};
345343

344+
/**
345+
* Exporting public key
346+
*
347+
* @param format
348+
* @returns {*}
349+
*/
346350
NodeRSA.prototype.exportPublic = function (format) {
347351
if (!this.isPublic()) {
348352
throw Error("It is not public key");
@@ -362,10 +366,18 @@ module.exports = (function () {
362366
}
363367
};
364368

369+
/**
370+
* Returns key size in bits
371+
* @returns {int}
372+
*/
365373
NodeRSA.prototype.getKeySize = function () {
366374
return this.keyPair.keySize;
367375
};
368376

377+
/**
378+
* Returns max message length in bytes (for 1 chunk) depending on current encryption scheme
379+
* @returns {int}
380+
*/
369381
NodeRSA.prototype.getMaxMessageSize = function () {
370382
return this.keyPair.maxMessageLength;
371383
};

src/formats/formats.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,46 @@
1+
var _ = require('lodash');
12
module.exports = {
23
pkcs1: require('./pkcs1'),
34
pkcs8: require('./pkcs8'),
45

5-
isPrivateExport: function(format) {
6+
isPrivateExport: function (format) {
67
return module.exports[format] && typeof module.exports[format].privateExport === 'function';
78
},
89

9-
isPrivateImport: function(format) {
10+
isPrivateImport: function (format) {
1011
return module.exports[format] && typeof module.exports[format].privateImport === 'function';
1112
},
1213

13-
isPublicExport: function(format) {
14+
isPublicExport: function (format) {
1415
return module.exports[format] && typeof module.exports[format].publicExport === 'function';
1516
},
1617

17-
isPublicImport: function(format) {
18+
isPublicImport: function (format) {
1819
return module.exports[format] && typeof module.exports[format].publicImport === 'function';
20+
},
21+
22+
detectAndImport: function (key, data, format) {
23+
if (format === undefined && _.isString(data)) {
24+
for (var format in module.exports) {
25+
if (typeof module.exports[format].autoImport === 'function' && module.exports[format].autoImport(key, data)) {
26+
return true;
27+
}
28+
}
29+
} else if (format) {
30+
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;
33+
if (module.exports[fmt[0]]) {
34+
if (keyType === 'private') {
35+
module.exports[fmt[0]].privateImport(key, data, keyOpt);
36+
} else {
37+
module.exports[fmt[0]].publicImport(key, data, keyOpt);
38+
}
39+
} else {
40+
throw Error('Unsupported key format');
41+
}
42+
}
43+
44+
return false;
1945
}
2046
};

src/formats/pkcs1.js

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,11 @@
11
var ber = require('asn1').Ber;
2+
var _ = require('lodash');
23
var utils = require('../utils');
34

45
module.exports = {
5-
privateExport: function(key, options) {
6+
privateExport: function (key, options) {
67
options = options || {};
78

8-
var der = module.exports.privateDerEncode(key);
9-
if (options.binary) {
10-
return der;
11-
} else {
12-
return '-----BEGIN RSA PRIVATE KEY-----\n' + utils.linebrk(der.toString('base64'), 64) + '\n-----END RSA PRIVATE KEY-----';
13-
}
14-
},
15-
16-
publicExport: function(key, options) {
17-
options = options || {};
18-
19-
var der = module.exports.publicDerEncode(key);
20-
if (options.binary) {
21-
return der;
22-
} else {
23-
return '-----BEGIN RSA PUBLIC KEY-----\n' + utils.linebrk(der.toString('base64'), 64) + '\n-----END RSA PUBLIC KEY-----';
24-
}
25-
},
26-
27-
privateDerEncode: function(key) {
289
var n = key.n.toBuffer();
2910
var d = key.d.toBuffer();
3011
var p = key.p.toBuffer();
@@ -48,10 +29,45 @@ module.exports = {
4829
writer.writeBuffer(coeff, 2);
4930
writer.endSequence();
5031

51-
return writer.buffer;
32+
if (options.binary) {
33+
return writer.buffer;
34+
} else {
35+
return '-----BEGIN RSA PRIVATE KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END RSA PRIVATE KEY-----';
36+
}
5237
},
5338

54-
publicDerEncode: function (key) {
39+
privateImport: function (key, data) {
40+
var buffer;
41+
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');
47+
} else if (Buffer.isBuffer(data)) {
48+
buffer = data;
49+
} else {
50+
throw Error('Unsupported key format');
51+
}
52+
53+
var reader = new ber.Reader(buffer);
54+
reader.readSequence();
55+
reader.readString(2, true); // just zero
56+
key.setPrivate(
57+
reader.readString(2, true), // modulus
58+
reader.readString(2, true), // publicExponent
59+
reader.readString(2, true), // privateExponent
60+
reader.readString(2, true), // prime1
61+
reader.readString(2, true), // prime2
62+
reader.readString(2, true), // exponent1 -- d mod (p1)
63+
reader.readString(2, true), // exponent2 -- d mod (q-1)
64+
reader.readString(2, true) // coefficient -- (inverse of q) mod p
65+
);
66+
},
67+
68+
publicExport: function (key, options) {
69+
options = options || {};
70+
5571
var n = key.n.toBuffer();
5672
var length = n.length + 512; // magic
5773

@@ -60,6 +76,30 @@ module.exports = {
6076
bodyWriter.writeBuffer(n, 2);
6177
bodyWriter.writeInt(key.e);
6278
bodyWriter.endSequence();
63-
return bodyWriter.buffer;
79+
80+
if (options.binary) {
81+
return bodyWriter.buffer;
82+
} else {
83+
return '-----BEGIN RSA PUBLIC KEY-----\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n-----END RSA PUBLIC KEY-----';
84+
}
85+
},
86+
87+
/**
88+
* Trying autodetect and import key
89+
* @param key
90+
* @param data
91+
*/
92+
autoImport: function (key, data) {
93+
if (/^\s*-----BEGIN RSA PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PRIVATE KEY-----\s*$/g.test(data)) {
94+
module.exports.privateImport(key, data);
95+
return true;
96+
}
97+
98+
if (/^\s*-----BEGIN RSA PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PUBLIC KEY-----\s*$/g.test(data)) {
99+
module.exports.publicImport(key, data);
100+
return true;
101+
}
102+
103+
return false;
64104
}
65105
};

src/formats/pkcs8.js

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
11
var ber = require('asn1').Ber;
2+
var _ = require('lodash');
23
var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1';
34
var utils = require('../utils');
45

56
module.exports = {
67
publicExport: function(key, options) {
78
options = options || {};
89

9-
var der = module.exports.publicDerEncode(key);
10-
if (options.binary) {
11-
return der;
12-
} else {
13-
return '-----BEGIN PUBLIC KEY-----\n' + utils.linebrk(der.toString('base64'), 64) + '\n-----END PUBLIC KEY-----';
14-
}
15-
},
16-
17-
publicImport: function(key) {
18-
19-
},
20-
21-
publicDerEncode: function (key) {
2210
var n = key.n.toBuffer();
2311
var length = n.length + 512; // magic
2412

@@ -39,6 +27,59 @@ module.exports = {
3927
writer.writeBuffer(body, 3);
4028
writer.endSequence();
4129

42-
return writer.buffer;
30+
if (options.binary) {
31+
return writer.buffer;
32+
} else {
33+
return '-----BEGIN PUBLIC KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END PUBLIC KEY-----';
34+
}
35+
},
36+
37+
publicImport: function(key, data) {
38+
var buffer;
39+
40+
if (_.isString(data)) {
41+
var pem = data.replace('-----BEGIN PUBLIC KEY-----', '')
42+
.replace('-----END PUBLIC KEY-----', '')
43+
.replace(/\s+|\n\r|\n|\r$/gm, '');
44+
buffer = new Buffer(pem, 'base64');
45+
} else if (Buffer.isBuffer(data)) {
46+
buffer = data;
47+
} else {
48+
throw Error('Unsupported key format');
49+
}
50+
51+
var reader = new ber.Reader(buffer);
52+
reader.readSequence();
53+
var header = new ber.Reader(reader.readString(0x30, true));
54+
if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
55+
throw Error('Invalid Public key format');
56+
}
57+
58+
var body = new ber.Reader(reader.readString(0x03, true));
59+
body.readByte();
60+
body.readSequence();
61+
key.setPublic(
62+
body.readString(0x02, true), // modulus
63+
body.readString(0x02, true) // publicExponent
64+
);
65+
},
66+
67+
/**
68+
* Trying autodetect and import key
69+
* @param key
70+
* @param data
71+
*/
72+
autoImport: function (key, data) {
73+
if (/^\s*-----BEGIN PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PRIVATE KEY-----\s*$/g.test(data)) {
74+
module.exports.privateImport(key, data);
75+
return true;
76+
}
77+
78+
if (/^\s*-----BEGIN PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PUBLIC KEY-----\s*$/g.test(data)) {
79+
module.exports.publicImport(key, data);
80+
return true;
81+
}
82+
83+
return false;
4384
}
44-
};
85+
};

test/tests.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -254,21 +254,13 @@ describe("NodeRSA", function(){
254254
assert.equal(privateNodeRSA.exportPublic(), publicKeyPEM);
255255
});
256256

257-
it("should create key from buffer/fs.readFileSync output", function(){
257+
it("should create and load key from buffer/fs.readFileSync output", function(){
258258
var key = new NodeRSA(fs.readFileSync(fileKey));
259259
assert.equal(key.exportPrivate(), fileKeyPEM);
260260
key = new NodeRSA();
261261
key.importKey(fs.readFileSync(fileKey));
262262
assert.equal(key.exportPrivate(), fileKeyPEM);
263263
});
264-
265-
it("should load PEM from buffer/fs.readFileSync output", function(){
266-
var key = new NodeRSA();
267-
assert.equal(key.isEmpty(), true);
268-
key.importKey(fs.readFileSync(fileKey));
269-
assert.equal(key.isEmpty(), false);
270-
assert.equal(key.exportPrivate(), fileKeyPEM);
271-
});
272264
});
273265

274266
describe("Bad cases", function () {

0 commit comments

Comments
 (0)