Skip to content

Commit 578ea23

Browse files
committed
Merge pull request #2 from rzcoder/signing-wip
Merge signing functionality
2 parents f166ae2 + 6c6b8cc commit 578ea23

File tree

4 files changed

+418
-220
lines changed

4 files changed

+418
-220
lines changed

README.md

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ Based on jsbn library from Tom Wu http://www-cs-students.stanford.edu/~tjw/jsbn/
55

66
* Pure JavaScript
77
* No needed OpenSSL
8+
* Generating keys
89
* Supports long messages for encrypt/decrypt
10+
* Signing and verifying
911

1012

1113
## Installing
@@ -23,6 +25,13 @@ npm test
2325
## Usage
2426

2527
### Create instance
28+
```js
29+
var key = new NodeRSA([key], [options]);
30+
```
31+
**key** - parameters of a generated key or the key in PEM format.<br/>
32+
**options** - additional settings
33+
* **signingAlgorithm** - algorithm used for signing and verifying. Default *'RSA-SHA256'*
34+
2635
#### "Empty" key
2736
```js
2837
var key = new NodeRSA();
@@ -58,8 +67,8 @@ key.loadFromPEM(pem_string);
5867

5968
### Export keys
6069
```js
61-
key.toPrivatePEM();
62-
key.toPublicPEM();
70+
key.getPrivatePEM();
71+
key.getPublicPEM();
6372
```
6473

6574
### Test key
@@ -71,18 +80,34 @@ key.isPublic([strict]);
7180

7281
### Encrypting/decrypting
7382
```js
74-
key.encrypt(buffer, [source_encoding], [output_encoding]);
83+
key.encrypt(buffer, [encoding], [source_encoding]);
7584
```
85+
Return encrypted data.<br/>
7686
**buffer** - data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.<br/>
77-
**source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). *Utf8* by default.<br/>
78-
**output_encoding** - encoding for output result, can also take 'buffer' to return Buffer object. Default *base64*.
87+
**encoding** - encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default *buffer*.
88+
**source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). *'utf8'* by default.<br/>
7989

8090
```js
8191
key.decrypt(buffer, [encoding]);
8292
```
83-
93+
Return decrypted data.<br/>
8494
**buffer** - data for decrypting. Takes Buffer object or base64 encoded string.<br/>
85-
**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result.
95+
**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default *'buffer'*.
96+
97+
### Signing/Verifying
98+
```js
99+
key.sign(buffer, [encoding], [source_encoding]);
100+
```
101+
Return signature for buffer. All the arguments are the same as for `encrypt` method.
102+
103+
```js
104+
key.verify(buffer, signature, [source_encoding], [signature_encoding])
105+
```
106+
Return result of check, _true_ or _false_.<br/>
107+
**buffer** - data for check, same as `encrypt` method.<br/>
108+
**signature** - signature for check, result of `sign` method.<br/>
109+
**source_encoding** - same as for`encrypt` method.<br/>
110+
**signature_encoding** - encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default *'buffer'*.
86111

87112
## Contributing
88113

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-rsa",
3-
"version": "0.1.32",
3+
"version": "0.1.40",
44
"description": "Node.js RSA library",
55
"main": "src/NodeRSA.js",
66
"scripts": {
@@ -16,7 +16,9 @@
1616
"crypto",
1717
"assymetric",
1818
"encryption",
19-
"decryption"
19+
"decryption",
20+
"sign",
21+
"verify"
2022
],
2123
"author": "rzcoder",
2224
"license": "BSD",

src/NodeRSA.js

Lines changed: 171 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
var rsa = require('./libs/rsa.js');
11+
var crypt = require('crypto');
1112
var ber = require('asn1').Ber;
1213
var _ = require('lodash');
1314
var utils = require('./utils');
@@ -16,16 +17,21 @@ var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1';
1617

1718
module.exports = (function() {
1819
/**
19-
* @param arg {string|object} Key in PEM format, or data for generate key {b: bits, e: exponent}
20+
* @param key {string|object} Key in PEM format, or data for generate key {b: bits, e: exponent}
2021
* @constructor
2122
*/
22-
function NodeRSA(arg) {
23+
function NodeRSA(key, options) {
2324
this.keyPair = new rsa.Key();
25+
this.$cache = {};
2426

25-
if (_.isObject(arg)) {
26-
this.generateKeyPair(arg.b, arg.e);
27-
} else if (_.isString(arg)) {
28-
this.loadFromPEM(arg);
27+
this.options = _.merge({
28+
signingAlgorithm: 'RSA-SHA256'
29+
}, options || {});
30+
31+
if (_.isObject(key)) {
32+
this.generateKeyPair(key.b, key.e);
33+
} else if (_.isString(key)) {
34+
this.loadFromPEM(key);
2935
}
3036
}
3137

@@ -41,6 +47,7 @@ module.exports = (function() {
4147
exp = 65537;
4248

4349
this.keyPair.generate(bits, exp.toString(16));
50+
this.$recalculateCache();
4451
return this;
4552
};
4653

@@ -55,6 +62,8 @@ module.exports = (function() {
5562
this.loadFromPublicPEM(pem, 'base64');
5663
} else
5764
throw Error('Invalid PEM format');
65+
66+
this.$recalculateCache();
5867
};
5968

6069
/**
@@ -106,9 +115,157 @@ module.exports = (function() {
106115
};
107116

108117
/**
118+
* Check if keypair contains private key
119+
*/
120+
NodeRSA.prototype.isPrivate = function() {
121+
return this.keyPair.n && this.keyPair.e && this.keyPair.d || false;
122+
};
123+
124+
/**
125+
* Check if keypair contains public key
126+
* @param strict {boolean} - public key only, return false if have private exponent
127+
*/
128+
NodeRSA.prototype.isPublic = function(strict) {
129+
return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d) || false;
130+
};
131+
132+
/**
133+
* Encrypting data method
134+
*
135+
* @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string.
136+
* @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
137+
* @param source_encoding {string} - optional. Encoding for given string. Default utf8.
138+
* @returns {string|Buffer}
139+
*/
140+
NodeRSA.prototype.encrypt = function(buffer, encoding, source_encoding) {
141+
var res = this.keyPair.encrypt(this.$getDataForEcrypt(buffer, source_encoding));
142+
143+
if (encoding == 'buffer' || !encoding) {
144+
return res;
145+
} else {
146+
return res.toString(encoding);
147+
}
148+
};
149+
150+
/**
151+
* Decrypting data method
152+
*
153+
* @param buffer {Buffer} - buffer for decrypting
154+
* @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type
155+
* @returns {Buffer|object|string}
156+
*/
157+
NodeRSA.prototype.decrypt = function(buffer, encoding) {
158+
buffer = _.isString(buffer) ? new Buffer(buffer, 'base64') : buffer;
159+
return this.$getDecryptedData(this.keyPair.decrypt(buffer), encoding);
160+
};
161+
162+
/**
163+
* Signing data
164+
*
165+
* @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string.
166+
* @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
167+
* @param source_encoding {string} - optional. Encoding for given string. Default utf8.
168+
* @returns {string|Buffer}
169+
*/
170+
NodeRSA.prototype.sign = function(buffer, encoding, source_encoding) {
171+
if (!this.isPrivate()) {
172+
throw Error("It is not private key");
173+
}
174+
175+
encoding = (!encoding || encoding == 'buffer' ? null : encoding);
176+
var signer = crypt.createSign(this.options.signingAlgorithm);
177+
signer.update(this.$getDataForEcrypt(buffer, source_encoding));
178+
return signer.sign(this.getPrivatePEM(), encoding);
179+
};
180+
181+
/**
182+
* Verifying signed data
183+
*
184+
* @param buffer - signed data
185+
* @param signature
186+
* @param source_encoding {string} - optional. Encoding for given string. Default utf8.
187+
* @param signature_encoding - optional. Encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
188+
* @returns {*}
189+
*/
190+
NodeRSA.prototype.verify = function(buffer, signature, source_encoding, signature_encoding) {
191+
signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding);
192+
var verifier = crypt.createVerify(this.options.signingAlgorithm);
193+
verifier.update(this.$getDataForEcrypt(buffer, source_encoding));
194+
return verifier.verify(this.getPublicPEM(), signature, signature_encoding);
195+
};
196+
197+
NodeRSA.prototype.getPrivatePEM = function () {
198+
if (!this.isPrivate()) {
199+
throw Error("It is not private key");
200+
}
201+
202+
return this.$cache.privatePEM;
203+
};
204+
205+
NodeRSA.prototype.getPublicPEM = function () {
206+
if (!this.isPublic()) {
207+
throw Error("It is not public key");
208+
}
209+
210+
return this.$cache.publicPEM;
211+
};
212+
213+
/**
214+
* Preparing given data for encrypting/signing. Just make new/return Buffer object.
215+
*
216+
* @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string.
217+
* @param encoding {string} - optional. Encoding for given string. Default utf8.
218+
* @returns {Buffer}
219+
*/
220+
NodeRSA.prototype.$getDataForEcrypt = function(buffer, encoding) {
221+
if (_.isString(buffer) || _.isNumber(buffer)) {
222+
return new Buffer('' + buffer, encoding || 'utf8');
223+
} else if (Buffer.isBuffer(buffer)) {
224+
return buffer;
225+
} else if (_.isObject(buffer)) {
226+
return new Buffer(JSON.stringify(buffer));
227+
} else {
228+
throw Error("Unexpected data type");
229+
}
230+
};
231+
232+
/**
233+
*
234+
* @param buffer {Buffer} - decrypted data.
235+
* @param encoding - optional. Encoding for result output. May be 'buffer', 'json' or any of Node.js Buffer supported encoding.
236+
* @returns {*}
237+
*/
238+
NodeRSA.prototype.$getDecryptedData = function(buffer, encoding) {
239+
encoding = encoding || 'buffer';
240+
241+
if (encoding == 'buffer') {
242+
return buffer;
243+
} else if (encoding == 'json') {
244+
return JSON.parse(buffer.toString());
245+
} else {
246+
return buffer.toString(encoding);
247+
}
248+
};
249+
250+
251+
/**
252+
* private
253+
* Recalculating properties
254+
*/
255+
NodeRSA.prototype.$recalculateCache = function() {
256+
this.$cache.privatePEM = this.$makePrivatePEM();
257+
this.$cache.publicPEM = this.$makePublicPEM();
258+
};
259+
260+
/**
261+
* private
109262
* @returns {string} private PEM string
110263
*/
111-
NodeRSA.prototype.toPrivatePEM = function() {
264+
NodeRSA.prototype.$makePrivatePEM = function() {
265+
if (!this.isPrivate()) {
266+
return null;
267+
}
268+
112269
var n = this.keyPair.n.toBuffer();
113270
var d = this.keyPair.d.toBuffer();
114271
var p = this.keyPair.p.toBuffer();
@@ -138,9 +295,14 @@ module.exports = (function() {
138295
};
139296

140297
/**
298+
* private
141299
* @returns {string} public PEM string
142300
*/
143-
NodeRSA.prototype.toPublicPEM = function() {
301+
NodeRSA.prototype.$makePublicPEM = function() {
302+
if (!this.isPublic()) {
303+
return null;
304+
}
305+
144306
var n = this.keyPair.n.toBuffer();
145307
var length = n.length + 512; // magic
146308

@@ -161,75 +323,10 @@ module.exports = (function() {
161323
writer.writeBuffer(body, 3);
162324
writer.endSequence();
163325

164-
n = writer.buffer.toString('hex');
165-
166326
return '-----BEGIN PUBLIC KEY-----\n' +
167327
utils.linebrk(writer.buffer.toString('base64'), 64) +
168328
'\n-----END PUBLIC KEY-----';
169329
};
170330

171-
/**
172-
* Check if keypair contains private key
173-
*/
174-
NodeRSA.prototype.isPrivate = function() {
175-
return this.keyPair.n && this.keyPair.e && this.keyPair.d;
176-
};
177-
178-
/**
179-
* Check if keypair contains public key
180-
* @param strict {boolean} - public key only, return false if have private exponent
181-
*/
182-
NodeRSA.prototype.isPublic = function(strict) {
183-
return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d);
184-
};
185-
186-
/**
187-
* Encrypting data method
188-
*
189-
* @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string.
190-
* @param source_encoding {string} - optional. Encoding for given string. Default utf8.
191-
* @param output_encoding {string} - optional. Encoding for output result, can also take 'buffer' to return Buffer object. Default base64.
192-
* @returns {string|Buffer}
193-
*/
194-
NodeRSA.prototype.encrypt = function(buffer, source_encoding, output_encoding) {
195-
var res = null;
196-
197-
if (_.isString(buffer) || _.isNumber(buffer)) {
198-
res = this.keyPair.encrypt(new Buffer('' + buffer, source_encoding || 'utf8'));
199-
} else if (Buffer.isBuffer(buffer)) {
200-
res = this.keyPair.encrypt(buffer);
201-
} else if (_.isObject(buffer)) {
202-
res = this.keyPair.encrypt(new Buffer(JSON.stringify(buffer)));
203-
}
204-
205-
if (output_encoding == 'buffer') {
206-
return res;
207-
} else {
208-
return res.toString(output_encoding || 'base64');
209-
}
210-
};
211-
212-
/**
213-
* Decrypting data method
214-
*
215-
* @param buffer {Buffer} - buffer for decrypting
216-
* @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type
217-
* @returns {Buffer|object|string}
218-
*/
219-
NodeRSA.prototype.decrypt = function(buffer, encoding) {
220-
encoding = encoding || 'utf8';
221-
222-
buffer = _.isString(buffer) ? new Buffer(buffer, 'base64') : buffer;
223-
var res = this.keyPair.decrypt(buffer);
224-
225-
if (encoding == 'buffer') {
226-
return res;
227-
} else if (encoding == 'json') {
228-
return JSON.parse(res.toString());
229-
} else {
230-
return res.toString(encoding);
231-
}
232-
};
233-
234-
return NodeRSA;
331+
return NodeRSA;
235332
})();

0 commit comments

Comments
 (0)