diff --git a/README.md b/README.md
index c0bfa20..a40b62a 100644
--- a/README.md
+++ b/README.md
@@ -46,22 +46,23 @@ This library developed and tested primary for Node.js, but it still can work in
```javascript
var NodeRSA = require('node-rsa');
-var key = new NodeRSA([key], [options]);
+var key = new NodeRSA([keyData, [format]], [options]);
```
-**key** - parameters of a generated key or the key in PEM format.
-**options** - additional settings
+* keyData — `{string|buffer|object}` — parameters for generating key or the key in one of supported formats.
+* format — `{string}` — format for importing key. See more details about formats in [Export/Import](#importexport-keys) section.
+* options — `{object}` — additional settings.
#### Options
-You can specify some options by second constructor argument, or over `key.setOptions()` method.
+You can specify some options by second/third constructor argument, or over `key.setOptions()` method.
-* **environment** - working environment, `'browser'` or `'node'`. Default autodetect.
-* **encryptionScheme** - padding scheme for encrypt/decrypt. Can be `'pkcs1_oaep'` or `'pkcs1'`. Default `'pkcs1_oaep'`.
-* **signingScheme** - scheme used for signing and verifying. Can be `'pkcs1'` or `'pss'` or 'scheme-hash' format string (eg `'pss-sha1'`). Default `'pkcs1-sha256'`, or, if chosen pss: `'pss-sha1'`.
+* environment — working environment, `'browser'` or `'node'`. Default autodetect.
+* encryptionScheme — padding scheme for encrypt/decrypt. Can be `'pkcs1_oaep'` or `'pkcs1'`. Default `'pkcs1_oaep'`.
+* signingScheme — scheme used for signing and verifying. Can be `'pkcs1'` or `'pss'` or 'scheme-hash' format string (eg `'pss-sha1'`). Default `'pkcs1-sha256'`, or, if chosen pss: `'pss-sha1'`.
**Advanced options:**
You also can specify advanced options for some schemes like this:
-```
+```javascript
options = {
encryptionScheme: {
scheme: 'pkcs1_oaep', //scheme
@@ -78,7 +79,6 @@ options = {
This lib supporting next hash algorithms: `'md5'`, `'ripemd160'`, `'sha1'`, `'sha256'`, `'sha512'` in browser and node environment and additional `'md4'`, `'sha'`, `'sha224'`, `'sha384'` in node only.
-
#### Creating "empty" key
```javascript
var key = new NodeRSA();
@@ -89,6 +89,15 @@ var key = new NodeRSA();
var key = new NodeRSA({b: 512});
```
+Also you can use next method:
+
+```javascript
+key.generateKeyPair([bits], [exp]);
+```
+
+* bits — `{int}` — key size in bits. 2048 by default.
+* exp — `{int}` — public exponent. 65537 by default.
+
#### Load key from PEM string
```javascript
@@ -103,19 +112,45 @@ var key = new NodeRSA('-----BEGIN RSA PRIVATE KEY-----\n'+
'-----END RSA PRIVATE KEY-----');
```
-Also you can use next methods:
-
+### Import/Export keys
```javascript
-key.generateKeyPair([bits], [exp]);
-key.importKey(pem_string|buffer_contains_pem);
+key.importKey(keyData, [format]);
+key.exportKey([format]);
```
-**bits** - key size in bits. 2048 by default.
-**exp** - public exponent. 65537 by default.
-### Export keys
+* keyData — `{string|buffer}` — key in PEM string **or** Buffer contains PEM string **or** Buffer contains DER encoded data.
+* format — `{string}` — format id for export/import.
+
+#### Format string syntax
+Format string composed of several parts: `scheme-[key_type]-[output_type]`
+
+Scheme — NodeRSA supports multiple format schemes for import/export keys:
+
+ * `'pkcs1'` — public key starts from `'-----BEGIN RSA PUBLIC KEY-----'` header and private key starts from `'-----BEGIN RSA PRIVATE KEY-----' header`
+ * `'pkcs8'` — public key starts from `'-----BEGIN PUBLIC KEY-----'` header and private key starts from `'-----BEGIN PRIVATE KEY-----' header`
+
+Key type — can be `'private'` or `'public'`. Default `'private'`
+Output type — can be:
+
+ * `'pem'` — Base64 encoded string with header and footer. Used by default.
+ * `'der'` — Binary encoded key data.
+
+**Notice:** For import, if *keyData* is PEM string or buffer containing string, you can do not specify format, but if you provide *keyData* as DER you must specify it in format string.
+
+**Shortcuts and examples**
+ * `'private'` or `'pkcs1'` or `'pkcs1-private'` == `'pkcs1-private-pem'` — private key encoded in pcks1 scheme as pem string.
+ * `'public'` or `'pkcs8-public'` == `'pkcs8-public-pem'` — public key encoded in pcks8 scheme as pem string.
+ * `'pkcs8'` or `'pkcs8-private'` == `'pkcs8-private-pem'` — private key encoded in pcks8 scheme as pem string.
+ * `'pkcs1-der'` == `'pkcs1-private-der'` — private key encoded in pcks1 scheme as binary buffer.
+ * `'pkcs8-public-der'` — public key encoded in pcks8 scheme as binary buffer.
+
+**Code example**
+
```javascript
-key.exportPrivate();
-key.exportPublic();
+var keyData = '-----BEGIN PUBLIC KEY----- ... -----BEGIN PRIVATE KEY-----';
+key.importKey(keyData, 'pkcs8');
+var publicDer = key.exportKey('pkcs8-public-der');
+var privateDer = key.exportKey('pkcs1-der');
```
### Properties
@@ -125,7 +160,7 @@ key.exportPublic();
key.isPrivate();
key.isPublic([strict]);
```
-**strict** - if true method will return false if key pair have private exponent. Default `false`.
+strict — `{boolean}` — if true method will return false if key pair have private exponent. Default `false`.
```javascript
key.isEmpty();
@@ -149,16 +184,18 @@ Return max data size for encrypt in bytes.
key.encrypt(buffer, [encoding], [source_encoding]);
```
Return encrypted data.
-**buffer** - data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.
-**encoding** - encoding for output result, may be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
-**source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). `'utf8'` by default.
+
+* buffer — `{buffer}` — data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.
+* encoding — `{string}` — encoding for output result, may be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
+* source_encoding — `{string}` — source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). `'utf8'` by default.
```javascript
key.decrypt(buffer, [encoding]);
```
Return decrypted data.
-**buffer** - data for decrypting. Takes Buffer object or base64 encoded string.
-**encoding** - encoding for result string. Can also take `'buffer'` for raw Buffer object, or `'json'` for automatic JSON.parse result. Default `'buffer'`.
+
+* buffer — `{buffer}` — data for decrypting. Takes Buffer object or base64 encoded string.
+* encoding — `{string}` — encoding for result string. Can also take `'buffer'` for raw Buffer object, or `'json'` for automatic JSON.parse result. Default `'buffer'`.
### Signing/Verifying
```javascript
@@ -170,10 +207,11 @@ Return signature for buffer. All the arguments are the same as for `encrypt` met
key.verify(buffer, signature, [source_encoding], [signature_encoding])
```
Return result of check, `true` or `false`.
-**buffer** - data for check, same as `encrypt` method.
-**signature** - signature for check, result of `sign` method.
-**source_encoding** - same as for `encrypt` method.
-**signature_encoding** - encoding of given signature. May be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
+
+* buffer — `{buffer}` — data for check, same as `encrypt` method.
+* signature — `{string}` — signature for check, result of `sign` method.
+* source_encoding — `{string}` — same as for `encrypt` method.
+* signature_encoding — `{string}` — encoding of given signature. May be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
## Contributing
@@ -181,6 +219,11 @@ Questions, comments, bug reports, and pull requests are all welcome.
## Changelog
+### 0.2.10
+ * **Methods `.exportPrivate()` and `.exportPublic()` was replaced by `.exportKey([format])`.**
+ * By default `.exportKey()` returns private key as `.exportPrivate()`, if you need public key from `.exportPublic()` you must specify format as `'public'` or `'pkcs8-public-pem'`.
+ * Method `.importKey(key, [format])` now has second argument.
+
### 0.2.0
* **`.getPublicPEM()` method was renamed to `.exportPublic()`**
* **`.getPrivatePEM()` method was renamed to `.exportPrivate()`**
diff --git a/package.json b/package.json
index d3be58c..608ea37 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "node-rsa",
- "version": "0.2.0",
+ "version": "0.2.10",
"description": "Node.js RSA library",
"main": "src/NodeRSA.js",
"scripts": {
diff --git a/src/NodeRSA.js b/src/NodeRSA.js
index 97528e2..572d11d 100644
--- a/src/NodeRSA.js
+++ b/src/NodeRSA.js
@@ -13,8 +13,7 @@ var ber = require('asn1').Ber;
var _ = require('lodash');
var utils = require('./utils');
var schemes = require('./schemes/schemes.js');
-
-var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1';
+var formats = require('./formats/formats.js');
module.exports = (function () {
var SUPPORTED_HASH_ALGORITHMS = {
@@ -25,15 +24,28 @@ module.exports = (function () {
var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1_oaep';
var DEFAULT_SIGNING_SCHEME = 'pkcs1';
+ var DEFAULT_EXPORT_FORMAT = 'private';
+ var EXPORT_FORMAT_ALIASES = {
+ 'private': 'pkcs1-private-pem',
+ 'private-der': 'pkcs1-private-der',
+ 'public': 'pkcs8-public-pem',
+ 'public-der': 'pkcs8-public-der'
+ };
+
/**
* @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent}
* @constructor
*/
- function NodeRSA(key, options) {
+ function NodeRSA(key, format, options) {
if (!this instanceof NodeRSA) {
return new NodeRSA(key, options);
}
+ if (_.isObject(format)) {
+ options = format;
+ format = undefined;
+ }
+
this.$options = {
signingScheme: DEFAULT_SIGNING_SCHEME,
signingSchemeOptions: {
@@ -49,14 +61,15 @@ module.exports = (function () {
rsaUtils: this
};
this.keyPair = new rsa.Key();
- this.setOptions(options);
this.$cache = {};
if (Buffer.isBuffer(key) || _.isString(key)) {
- this.importKey(key);
+ this.importKey(key, format);
} else if (_.isObject(key)) {
this.generateKeyPair(key.b, key.e);
}
+
+ this.setOptions(options);
}
/**
@@ -98,6 +111,7 @@ module.exports = (function () {
if (!schemes.isSignature(this.$options.signingScheme)) {
throw Error('Unsupported signing scheme');
}
+
if (this.$options.signingSchemeOptions.hash &&
_.indexOf(SUPPORTED_HASH_ALGORITHMS[this.$options.environment], this.$options.signingSchemeOptions.hash) == -1) {
throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment');
@@ -142,88 +156,51 @@ module.exports = (function () {
}
this.keyPair.generate(bits, exp.toString(16));
- this.$recalculateCache();
+ this.$cache = {};
return this;
};
/**
- * Load key from PEM string
- * @param pem {string}
+ * Importing key
+ * @param keyData {string|buffer}
+ * @param format {string}
*/
- NodeRSA.prototype.importKey = function (pem) {
- if (Buffer.isBuffer(pem)) {
- pem = pem.toString('utf8');
+ NodeRSA.prototype.importKey = function (keyData, format) {
+ if (!keyData) {
+ throw Error("Empty key given");
}
- if (/^\s*-----BEGIN RSA PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PRIVATE KEY-----\s*$/g.test(pem)) {
- this.$loadFromPrivatePEM(pem, 'base64');
- } else if (/^\s*-----BEGIN PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PUBLIC KEY-----\s*$/g.test(pem)) {
- this.$loadFromPublicPEM(pem, 'base64');
- } else
- throw Error('Invalid PEM format');
-
- this.$recalculateCache();
- };
+ if (format) {
+ format = EXPORT_FORMAT_ALIASES[format] || format;
+ }
- /**
- * Make key form private PEM string
- *
- * @param privatePEM {string}
- */
- NodeRSA.prototype.$loadFromPrivatePEM = function (privatePEM, encoding) {
- var pem = privatePEM
- .replace('-----BEGIN RSA PRIVATE KEY-----', '')
- .replace('-----END RSA PRIVATE KEY-----', '')
- .replace(/\s+|\n\r|\n|\r$/gm, '');
- var reader = new ber.Reader(new Buffer(pem, 'base64'));
-
- reader.readSequence();
- reader.readString(2, true); // just zero
- this.keyPair.setPrivate(
- reader.readString(2, true), // modulus
- reader.readString(2, true), // publicExponent
- reader.readString(2, true), // privateExponent
- reader.readString(2, true), // prime1
- reader.readString(2, true), // prime2
- reader.readString(2, true), // exponent1 -- d mod (p1)
- reader.readString(2, true), // exponent2 -- d mod (q-1)
- reader.readString(2, true) // coefficient -- (inverse of q) mod p
- );
+ if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) {
+ throw Error("Key format must be specified");
+ }
+ this.$cache = {};
};
/**
- * Make key form public PEM string
- *
- * @param publicPEM {string}
+ * Exporting key
+ * @param format {string}
*/
- NodeRSA.prototype.$loadFromPublicPEM = function (publicPEM, encoding) {
- var pem = publicPEM
- .replace('-----BEGIN PUBLIC KEY-----', '')
- .replace('-----END PUBLIC KEY-----', '')
- .replace(/\s+|\n\r|\n|\r$/gm, '');
- var reader = new ber.Reader(new Buffer(pem, 'base64'));
-
- reader.readSequence();
- var header = new ber.Reader(reader.readString(0x30, true));
- if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
- throw Error('Invalid Public key PEM format');
+ NodeRSA.prototype.exportKey = function (format) {
+ format = format || DEFAULT_EXPORT_FORMAT;
+ format = EXPORT_FORMAT_ALIASES[format] || format;
+
+ if (!this.$cache[format]) {
+ this.$cache[format] = formats.detectAndExport(this.keyPair, format);
}
- var body = new ber.Reader(reader.readString(0x03, true));
- body.readByte();
- body.readSequence();
- this.keyPair.setPublic(
- body.readString(0x02, true), // modulus
- body.readString(0x02, true) // publicExponent
- );
+ return this.$cache[format];
};
/**
* Check if key pair contains private key
*/
NodeRSA.prototype.isPrivate = function () {
- return this.keyPair.n && this.keyPair.e && this.keyPair.d || false;
+ return this.keyPair.isPrivate();
};
/**
@@ -231,7 +208,7 @@ module.exports = (function () {
* @param strict {boolean} - public key only, return false if have private exponent
*/
NodeRSA.prototype.isPublic = function (strict) {
- return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d) || false;
+ return this.keyPair.isPublic(strict);
};
/**
@@ -251,7 +228,7 @@ module.exports = (function () {
*/
NodeRSA.prototype.encrypt = function (buffer, encoding, source_encoding) {
try {
- var res = this.keyPair.encrypt(this.$getDataForEcrypt(buffer, source_encoding));
+ var res = this.keyPair.encrypt(this.$getDataForEncrypt(buffer, source_encoding));
if (encoding == 'buffer' || !encoding) {
return res;
@@ -274,9 +251,11 @@ module.exports = (function () {
try {
buffer = _.isString(buffer) ? new Buffer(buffer, 'base64') : buffer;
var res = this.keyPair.decrypt(buffer);
+
if (res === null) {
throw Error('Key decrypt method returns null.');
}
+
return this.$getDecryptedData(res, encoding);
} catch (e) {
throw Error('Error during decryption (probably incorrect key). Original error: ' + e);
@@ -295,11 +274,13 @@ module.exports = (function () {
if (!this.isPrivate()) {
throw Error("It is not private key");
}
- var res = this.keyPair.sign(this.$getDataForEcrypt(buffer, source_encoding));
+
+ var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding));
if (encoding && encoding != 'buffer') {
res = res.toString(encoding);
}
+
return res;
};
@@ -317,27 +298,21 @@ module.exports = (function () {
throw Error("It is not public key");
}
signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding);
- return this.keyPair.verify(this.$getDataForEcrypt(buffer, source_encoding), signature, signature_encoding);
- };
-
- NodeRSA.prototype.exportPrivate = function () {
- if (!this.isPrivate()) {
- throw Error("It is not private key");
- }
- return this.$cache.privatePEM;
- };
-
- NodeRSA.prototype.exportPublic = function () {
- if (!this.isPublic()) {
- throw Error("It is not public key");
- }
- return this.$cache.publicPEM;
+ return this.keyPair.verify(this.$getDataForEncrypt(buffer, source_encoding), signature, signature_encoding);
};
+ /**
+ * Returns key size in bits
+ * @returns {int}
+ */
NodeRSA.prototype.getKeySize = function () {
return this.keyPair.keySize;
};
+ /**
+ * Returns max message length in bytes (for 1 chunk) depending on current encryption scheme
+ * @returns {int}
+ */
NodeRSA.prototype.getMaxMessageSize = function () {
return this.keyPair.maxMessageLength;
};
@@ -349,7 +324,7 @@ module.exports = (function () {
* @param encoding {string} - optional. Encoding for given string. Default utf8.
* @returns {Buffer}
*/
- NodeRSA.prototype.$getDataForEcrypt = function (buffer, encoding) {
+ NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) {
if (_.isString(buffer) || _.isNumber(buffer)) {
return new Buffer('' + buffer, encoding || 'utf8');
} else if (Buffer.isBuffer(buffer)) {
@@ -379,85 +354,5 @@ module.exports = (function () {
}
};
- /**
- * private
- * Recalculating properties
- */
- NodeRSA.prototype.$recalculateCache = function () {
- this.$cache.privatePEM = this.$makePrivatePEM();
- this.$cache.publicPEM = this.$makePublicPEM();
- };
-
- /**
- * private
- * @returns {string} private PEM string
- */
- NodeRSA.prototype.$makePrivatePEM = function () {
- if (!this.isPrivate()) {
- return null;
- }
-
- var n = this.keyPair.n.toBuffer();
- var d = this.keyPair.d.toBuffer();
- var p = this.keyPair.p.toBuffer();
- var q = this.keyPair.q.toBuffer();
- var dmp1 = this.keyPair.dmp1.toBuffer();
- var dmq1 = this.keyPair.dmq1.toBuffer();
- var coeff = this.keyPair.coeff.toBuffer();
-
- var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
- var writer = new ber.Writer({size: length});
-
- writer.startSequence();
- writer.writeInt(0);
- writer.writeBuffer(n, 2);
- writer.writeInt(this.keyPair.e);
- writer.writeBuffer(d, 2);
- writer.writeBuffer(p, 2);
- writer.writeBuffer(q, 2);
- writer.writeBuffer(dmp1, 2);
- writer.writeBuffer(dmq1, 2);
- writer.writeBuffer(coeff, 2);
- writer.endSequence();
-
- return '-----BEGIN RSA PRIVATE KEY-----\n' +
- utils.linebrk(writer.buffer.toString('base64'), 64) +
- '\n-----END RSA PRIVATE KEY-----';
- };
-
- /**
- * private
- * @returns {string} public PEM string
- */
- NodeRSA.prototype.$makePublicPEM = function () {
- if (!this.isPublic()) {
- return null;
- }
-
- var n = this.keyPair.n.toBuffer();
- var length = n.length + 512; // magic
-
- var bodyWriter = new ber.Writer({size: length});
- bodyWriter.writeByte(0);
- bodyWriter.startSequence();
- bodyWriter.writeBuffer(n, 2);
- bodyWriter.writeInt(this.keyPair.e);
- bodyWriter.endSequence();
- var body = bodyWriter.buffer;
-
- var writer = new ber.Writer({size: length});
- writer.startSequence();
- writer.startSequence();
- writer.writeOID(PUBLIC_RSA_OID);
- writer.writeNull();
- writer.endSequence();
- writer.writeBuffer(body, 3);
- writer.endSequence();
-
- return '-----BEGIN PUBLIC KEY-----\n' +
- utils.linebrk(writer.buffer.toString('base64'), 64) +
- '\n-----END PUBLIC KEY-----';
- };
-
return NodeRSA;
})();
diff --git a/src/formats/formats.js b/src/formats/formats.js
new file mode 100644
index 0000000..e975b2a
--- /dev/null
+++ b/src/formats/formats.js
@@ -0,0 +1,94 @@
+var _ = require('lodash');
+
+function formatParse(format) {
+ format = format.split('-');
+ var keyType = 'private';
+ var keyOpt = {type: 'default'};
+
+ for (var i = 1; i < format.length; i++) {
+ if (format[i]) {
+ switch (format[i]) {
+ case 'public':
+ keyType = format[i];
+ break;
+ case 'private':
+ keyType = format[i];
+ break;
+ case 'pem':
+ keyOpt.type = format[i];
+ break;
+ case 'der':
+ keyOpt.type = format[i];
+ break;
+ }
+ }
+ }
+
+ return {scheme: format[0], keyType: keyType, keyOpt: keyOpt};
+}
+
+module.exports = {
+ pkcs1: require('./pkcs1'),
+ pkcs8: require('./pkcs8'),
+
+ isPrivateExport: function (format) {
+ return module.exports[format] && typeof module.exports[format].privateExport === 'function';
+ },
+
+ isPrivateImport: function (format) {
+ return module.exports[format] && typeof module.exports[format].privateImport === 'function';
+ },
+
+ isPublicExport: function (format) {
+ return module.exports[format] && typeof module.exports[format].publicExport === 'function';
+ },
+
+ isPublicImport: function (format) {
+ return module.exports[format] && typeof module.exports[format].publicImport === 'function';
+ },
+
+ detectAndImport: function (key, data, format) {
+ if (format === undefined) {
+ for (var scheme in module.exports) {
+ if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) {
+ return true;
+ }
+ }
+ } else if (format) {
+ var fmt = formatParse(format);
+ if (module.exports[fmt.scheme]) {
+ if (fmt.keyType === 'private') {
+ module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt);
+ } else {
+ module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt);
+ }
+ } else {
+ throw Error('Unsupported key format');
+ }
+ }
+
+ return false;
+ },
+
+ detectAndExport: function (key, format) {
+ if (format) {
+ var fmt = formatParse(format);
+
+ if (module.exports[fmt.scheme]) {
+ if (fmt.keyType === 'private') {
+ if (!key.isPrivate()) {
+ throw Error("It is not private key");
+ }
+ return module.exports[fmt.scheme].privateExport(key, fmt.keyOpt);
+ } else {
+ if (!key.isPublic()) {
+ throw Error("It is not public key");
+ }
+ return module.exports[fmt.scheme].publicExport(key, fmt.keyOpt);
+ }
+ } else {
+ throw Error('Unsupported key format');
+ }
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/formats/pkcs1.js b/src/formats/pkcs1.js
new file mode 100644
index 0000000..715e7c0
--- /dev/null
+++ b/src/formats/pkcs1.js
@@ -0,0 +1,143 @@
+var ber = require('asn1').Ber;
+var _ = require('lodash');
+var utils = require('../utils');
+
+module.exports = {
+ privateExport: function (key, options) {
+ options = options || {};
+
+ var n = key.n.toBuffer();
+ var d = key.d.toBuffer();
+ var p = key.p.toBuffer();
+ var q = key.q.toBuffer();
+ var dmp1 = key.dmp1.toBuffer();
+ var dmq1 = key.dmq1.toBuffer();
+ var coeff = key.coeff.toBuffer();
+
+ var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
+ var writer = new ber.Writer({size: length});
+
+ writer.startSequence();
+ writer.writeInt(0);
+ writer.writeBuffer(n, 2);
+ writer.writeInt(key.e);
+ writer.writeBuffer(d, 2);
+ writer.writeBuffer(p, 2);
+ writer.writeBuffer(q, 2);
+ writer.writeBuffer(dmp1, 2);
+ writer.writeBuffer(dmq1, 2);
+ writer.writeBuffer(coeff, 2);
+ writer.endSequence();
+
+ if (options.type === 'der') {
+ return writer.buffer;
+ } else {
+ return '-----BEGIN RSA PRIVATE KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END RSA PRIVATE KEY-----';
+ }
+ },
+
+ privateImport: function (key, data, options) {
+ options = options || {};
+ var buffer;
+
+ if (options.type !== 'der') {
+ if (Buffer.isBuffer(data)) {
+ data = data.toString('utf8');
+ }
+
+ if (_.isString(data)) {
+ var pem = data.replace('-----BEGIN RSA PRIVATE KEY-----', '')
+ .replace('-----END RSA PRIVATE KEY-----', '')
+ .replace(/\s+|\n\r|\n|\r$/gm, '');
+ buffer = new Buffer(pem, 'base64');
+ } else {
+ throw Error('Unsupported key format');
+ }
+ } else if (Buffer.isBuffer(data)) {
+ buffer = data;
+ } else {
+ throw Error('Unsupported key format');
+ }
+
+ var reader = new ber.Reader(buffer);
+ reader.readSequence();
+ reader.readString(2, true); // just zero
+ key.setPrivate(
+ reader.readString(2, true), // modulus
+ reader.readString(2, true), // publicExponent
+ reader.readString(2, true), // privateExponent
+ reader.readString(2, true), // prime1
+ reader.readString(2, true), // prime2
+ reader.readString(2, true), // exponent1 -- d mod (p1)
+ reader.readString(2, true), // exponent2 -- d mod (q-1)
+ reader.readString(2, true) // coefficient -- (inverse of q) mod p
+ );
+ },
+
+ publicExport: function (key, options) {
+ options = options || {};
+
+ var n = key.n.toBuffer();
+ var length = n.length + 512; // magic
+
+ var bodyWriter = new ber.Writer({size: length});
+ bodyWriter.startSequence();
+ bodyWriter.writeBuffer(n, 2);
+ bodyWriter.writeInt(key.e);
+ bodyWriter.endSequence();
+
+ if (options.type === 'der') {
+ return bodyWriter.buffer;
+ } else {
+ return '-----BEGIN RSA PUBLIC KEY-----\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n-----END RSA PUBLIC KEY-----';
+ }
+ },
+
+ publicImport: function (key, data, options) {
+ options = options || {};
+ var buffer;
+
+ if (options.type !== 'der') {
+ if (Buffer.isBuffer(data)) {
+ data = data.toString('utf8');
+ }
+
+ if (_.isString(data)) {
+ var pem = data.replace('-----BEGIN RSA PUBLIC KEY-----', '')
+ .replace('-----END RSA PUBLIC KEY-----', '')
+ .replace(/\s+|\n\r|\n|\r$/gm, '');
+ buffer = new Buffer(pem, 'base64');
+ }
+ } else if (Buffer.isBuffer(data)) {
+ buffer = data;
+ } else {
+ throw Error('Unsupported key format');
+ }
+
+ var body = new ber.Reader(buffer);
+ body.readSequence();
+ key.setPublic(
+ body.readString(0x02, true), // modulus
+ body.readString(0x02, true) // publicExponent
+ );
+ },
+
+ /**
+ * Trying autodetect and import key
+ * @param key
+ * @param data
+ */
+ autoImport: function (key, data) {
+ if (/^\s*-----BEGIN RSA PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PRIVATE KEY-----\s*$/g.test(data)) {
+ module.exports.privateImport(key, data);
+ return true;
+ }
+
+ if (/^\s*-----BEGIN RSA PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END RSA PUBLIC KEY-----\s*$/g.test(data)) {
+ module.exports.publicImport(key, data);
+ return true;
+ }
+
+ return false;
+ }
+};
\ No newline at end of file
diff --git a/src/formats/pkcs8.js b/src/formats/pkcs8.js
new file mode 100644
index 0000000..9647879
--- /dev/null
+++ b/src/formats/pkcs8.js
@@ -0,0 +1,182 @@
+var ber = require('asn1').Ber;
+var _ = require('lodash');
+var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1';
+var utils = require('../utils');
+
+module.exports = {
+ privateExport: function (key, options) {
+ options = options || {};
+
+ var n = key.n.toBuffer();
+ var d = key.d.toBuffer();
+ var p = key.p.toBuffer();
+ var q = key.q.toBuffer();
+ var dmp1 = key.dmp1.toBuffer();
+ var dmq1 = key.dmq1.toBuffer();
+ var coeff = key.coeff.toBuffer();
+
+ var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
+ var bodyWriter = new ber.Writer({size: length});
+
+ bodyWriter.startSequence();
+ bodyWriter.writeInt(0);
+ bodyWriter.writeBuffer(n, 2);
+ bodyWriter.writeInt(key.e);
+ bodyWriter.writeBuffer(d, 2);
+ bodyWriter.writeBuffer(p, 2);
+ bodyWriter.writeBuffer(q, 2);
+ bodyWriter.writeBuffer(dmp1, 2);
+ bodyWriter.writeBuffer(dmq1, 2);
+ bodyWriter.writeBuffer(coeff, 2);
+ bodyWriter.endSequence();
+
+ var writer = new ber.Writer({size: length});
+ writer.startSequence();
+ writer.writeInt(0);
+ writer.startSequence();
+ writer.writeOID(PUBLIC_RSA_OID);
+ writer.writeNull();
+ writer.endSequence();
+ writer.writeBuffer(bodyWriter.buffer, 4);
+ writer.endSequence();
+
+ if (options.type === 'der') {
+ return writer.buffer;
+ } else {
+ return '-----BEGIN PRIVATE KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END PRIVATE KEY-----';
+ }
+ },
+
+ privateImport: function (key, data, options) {
+ options = options || {};
+ var buffer;
+
+ if (options.type !== 'der') {
+ if (Buffer.isBuffer(data)) {
+ data = data.toString('utf8');
+ }
+
+ if (_.isString(data)) {
+ var pem = data.replace('-----BEGIN PRIVATE KEY-----', '')
+ .replace('-----END PRIVATE KEY-----', '')
+ .replace(/\s+|\n\r|\n|\r$/gm, '');
+ buffer = new Buffer(pem, 'base64');
+ } else {
+ throw Error('Unsupported key format');
+ }
+ } else if (Buffer.isBuffer(data)) {
+ buffer = data;
+ } else {
+ throw Error('Unsupported key format');
+ }
+
+ var reader = new ber.Reader(buffer);
+ reader.readSequence();
+ reader.readInt(0);
+ var header = new ber.Reader(reader.readString(0x30, true));
+
+ if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
+ throw Error('Invalid Public key format');
+ }
+
+ var body = new ber.Reader(reader.readString(0x04, true));
+ body.readSequence();
+ body.readString(2, true); // just zero
+ key.setPrivate(
+ body.readString(2, true), // modulus
+ body.readString(2, true), // publicExponent
+ body.readString(2, true), // privateExponent
+ body.readString(2, true), // prime1
+ body.readString(2, true), // prime2
+ body.readString(2, true), // exponent1 -- d mod (p1)
+ body.readString(2, true), // exponent2 -- d mod (q-1)
+ body.readString(2, true) // coefficient -- (inverse of q) mod p
+ );
+ },
+
+ publicExport: function (key, options) {
+ options = options || {};
+
+ var n = key.n.toBuffer();
+ var length = n.length + 512; // magic
+
+ var bodyWriter = new ber.Writer({size: length});
+ bodyWriter.writeByte(0);
+ bodyWriter.startSequence();
+ bodyWriter.writeBuffer(n, 2);
+ bodyWriter.writeInt(key.e);
+ bodyWriter.endSequence();
+
+ var writer = new ber.Writer({size: length});
+ writer.startSequence();
+ writer.startSequence();
+ writer.writeOID(PUBLIC_RSA_OID);
+ writer.writeNull();
+ writer.endSequence();
+ writer.writeBuffer(bodyWriter.buffer, 3);
+ writer.endSequence();
+
+ if (options.type === 'der') {
+ return writer.buffer;
+ } else {
+ return '-----BEGIN PUBLIC KEY-----\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n-----END PUBLIC KEY-----';
+ }
+ },
+
+ publicImport: function (key, data, options) {
+ options = options || {};
+ var buffer;
+
+ if (options.type !== 'der') {
+ if (Buffer.isBuffer(data)) {
+ data = data.toString('utf8');
+ }
+
+ if (_.isString(data)) {
+ var pem = data.replace('-----BEGIN PUBLIC KEY-----', '')
+ .replace('-----END PUBLIC KEY-----', '')
+ .replace(/\s+|\n\r|\n|\r$/gm, '');
+ buffer = new Buffer(pem, 'base64');
+ }
+ } else if (Buffer.isBuffer(data)) {
+ buffer = data;
+ } else {
+ throw Error('Unsupported key format');
+ }
+
+ var reader = new ber.Reader(buffer);
+ reader.readSequence();
+ var header = new ber.Reader(reader.readString(0x30, true));
+
+ if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
+ throw Error('Invalid Public key format');
+ }
+
+ var body = new ber.Reader(reader.readString(0x03, true));
+ body.readByte();
+ body.readSequence();
+ key.setPublic(
+ body.readString(0x02, true), // modulus
+ body.readString(0x02, true) // publicExponent
+ );
+ },
+
+ /**
+ * Trying autodetect and import key
+ * @param key
+ * @param data
+ */
+ autoImport: function (key, data) {
+ if (/^\s*-----BEGIN PRIVATE KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PRIVATE KEY-----\s*$/g.test(data)) {
+ module.exports.privateImport(key, data);
+ return true;
+ }
+
+ if (/^\s*-----BEGIN PUBLIC KEY-----\s*([A-Za-z0-9+/=]+\s*)+-----END PUBLIC KEY-----\s*$/g.test(data)) {
+ module.exports.publicImport(key, data);
+ return true;
+ }
+
+ return false;
+ }
+};
diff --git a/src/libs/rsa.js b/src/libs/rsa.js
index 245bb29..fe1dccd 100644
--- a/src/libs/rsa.js
+++ b/src/libs/rsa.js
@@ -278,6 +278,21 @@ module.exports.Key = (function() {
return this.signingScheme.verify.apply(this.signingScheme, arguments);
};
+ /**
+ * Check if key pair contains private key
+ */
+ RSAKey.prototype.isPrivate = function () {
+ return this.n && this.e && this.d || false;
+ };
+
+ /**
+ * Check if key pair contains public key
+ * @param strict {boolean} - public key only, return false if have private exponent
+ */
+ RSAKey.prototype.isPublic = function (strict) {
+ return this.n && this.e && !(strict && this.d) || false;
+ };
+
Object.defineProperty(RSAKey.prototype, 'keySize', {
get: function() { return this.cache.keyBitLength; }
});
diff --git a/src/schemes/pkcs1.js b/src/schemes/pkcs1.js
index d46e297..d6907c6 100644
--- a/src/schemes/pkcs1.js
+++ b/src/schemes/pkcs1.js
@@ -115,7 +115,7 @@ module.exports.makeScheme = function (key, options) {
} else {
var signer = crypt.createSign('RSA-' + hashAlgorithm.toUpperCase());
signer.update(buffer);
- return signer.sign(this.options.rsaUtils.exportPrivate());
+ return signer.sign(this.options.rsaUtils.exportKey('private'));
}
};
@@ -137,7 +137,7 @@ module.exports.makeScheme = function (key, options) {
} else {
var verifier = crypt.createVerify('RSA-' + hashAlgorithm.toUpperCase());
verifier.update(buffer);
- return verifier.verify(this.options.rsaUtils.exportPublic(), signature, signature_encoding);
+ return verifier.verify(this.options.rsaUtils.exportKey('public'), signature, signature_encoding);
}
};
diff --git a/test/keys/private_pkcs1.der b/test/keys/private_pkcs1.der
new file mode 100644
index 0000000..3afd0ad
Binary files /dev/null and b/test/keys/private_pkcs1.der differ
diff --git a/test/private.key b/test/keys/private_pkcs1.pem
similarity index 100%
rename from test/private.key
rename to test/keys/private_pkcs1.pem
diff --git a/test/keys/private_pkcs8.der b/test/keys/private_pkcs8.der
new file mode 100644
index 0000000..633b0c0
Binary files /dev/null and b/test/keys/private_pkcs8.der differ
diff --git a/test/keys/private_pkcs8.pem b/test/keys/private_pkcs8.pem
new file mode 100644
index 0000000..af76cbe
--- /dev/null
+++ b/test/keys/private_pkcs8.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIJ1j4SkML+89rff
+mXvnVIEzx3kt/jPhBlfhtz4OpNj3NVj+uY8ZtOQK54qcgoCm44Yxck5oLA0f3BR+
+023tPR+gTCOXXq4gLybiHBKOM0vlMqoxV7JLK/TigG6J2/vq7nI2IsLgnzmVuZbd
+YDJiRMJub9UlkBc0m6oKbsBVaZAxAgMBAAECgYARhHeg9+sT0+vNBNk3KfjO4IUI
+YIShsrWHV2ejjZPVDly7dni0Hl+Lv2sX0aw0WjRy8HDI1nQTzxNeLSl2uURbqZ0+
+BAV3cIXGs6Aq30Aak60jXPNU5vVR13LCr+ZYE/IRPAQrOruULacBwxSvSuUWusOZ
+RPZ1p8d65acUxn/VgQJBAMK1+2wf3+mtuC5DgXaU5awvP3pqLH+OcjwWEGa6QqZ8
+sxeMJlhi/4OyvxMiX+KuIapxKAaQEcegZ7WeYtRngQcCQQCrhhwHxP0MshhR5pL4
+lFbAs56NTr+th2DEEhoafabQt2n/PuUszuBvQfkCX7s85qOQB2cNyUEOHLGoicc7
+hW8HAkEAnbmM0SmA2GpAqDlGxigXZENhyGf5Y9qf7sxwvMZk9zhMWubMqQyIrsY8
+weM2iQlFfCvtoGNUA8GMQTLsG+M8QQJAU4nitwoDMR7AZ4tEQ5uD0SDqdOpIwsxv
+na8vVX2jNH5QOse4OKWde7KHA9f6SoQOX4SrdXRyodvpzysjhOLNGwJAA0jKGgnf
+e6CE3NsIiSeQw8q2C8s5QBGdvJr9uR9F7EuJzJjNc826pkNjwllAeWyE3xwJxlxi
+MY9hixx3YsoHqg==
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/test/keys/public_pkcs1.der b/test/keys/public_pkcs1.der
new file mode 100644
index 0000000..58891a8
Binary files /dev/null and b/test/keys/public_pkcs1.der differ
diff --git a/test/keys/public_pkcs1.pem b/test/keys/public_pkcs1.pem
new file mode 100644
index 0000000..8b6b542
--- /dev/null
+++ b/test/keys/public_pkcs1.pem
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBAIJ1j4SkML+89rffmXvnVIEzx3kt/jPhBlfhtz4OpNj3NVj+uY8ZtOQK
+54qcgoCm44Yxck5oLA0f3BR+023tPR+gTCOXXq4gLybiHBKOM0vlMqoxV7JLK/Ti
+gG6J2/vq7nI2IsLgnzmVuZbdYDJiRMJub9UlkBc0m6oKbsBVaZAxAgMBAAE=
+-----END RSA PUBLIC KEY-----
\ No newline at end of file
diff --git a/test/keys/public_pkcs8.der b/test/keys/public_pkcs8.der
new file mode 100644
index 0000000..5d2c1c3
Binary files /dev/null and b/test/keys/public_pkcs8.der differ
diff --git a/test/keys/public_pkcs8.pem b/test/keys/public_pkcs8.pem
new file mode 100644
index 0000000..a08ccf6
--- /dev/null
+++ b/test/keys/public_pkcs8.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCdY+EpDC/vPa335l751SBM8d5
+Lf4z4QZX4bc+DqTY9zVY/rmPGbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwj
+l16uIC8m4hwSjjNL5TKqMVeySyv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/V
+JZAXNJuqCm7AVWmQMQIDAQAB
+-----END PUBLIC KEY-----
\ No newline at end of file
diff --git a/test/private_pkcs1.pem b/test/private_pkcs1.pem
new file mode 100644
index 0000000..2ea486c
--- /dev/null
+++ b/test/private_pkcs1.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmP
+GbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVey
+Syv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQIDAQAB
+AoGAEYR3oPfrE9PrzQTZNyn4zuCFCGCEobK1h1dno42T1Q5cu3Z4tB5fi79rF9Gs
+NFo0cvBwyNZ0E88TXi0pdrlEW6mdPgQFd3CFxrOgKt9AGpOtI1zzVOb1Uddywq/m
+WBPyETwEKzq7lC2nAcMUr0rlFrrDmUT2dafHeuWnFMZ/1YECQQDCtftsH9/prbgu
+Q4F2lOWsLz96aix/jnI8FhBmukKmfLMXjCZYYv+Dsr8TIl/iriGqcSgGkBHHoGe1
+nmLUZ4EHAkEAq4YcB8T9DLIYUeaS+JRWwLOejU6/rYdgxBIaGn2m0Ldp/z7lLM7g
+b0H5Al+7POajkAdnDclBDhyxqInHO4VvBwJBAJ25jNEpgNhqQKg5RsYoF2RDYchn
++WPan+7McLzGZPc4TFrmzKkMiK7GPMHjNokJRXwr7aBjVAPBjEEy7BvjPEECQFOJ
+4rcKAzEewGeLREObg9Eg6nTqSMLMb52vL1V9ozR+UDrHuDilnXuyhwPX+kqEDl+E
+q3V0cqHb6c8rI4TizRsCQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL
+icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6o=
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/test/tests.js b/test/tests.js
index 90c1d76..f6aea3a 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -159,8 +159,8 @@ describe("NodeRSA", function(){
}
});
- describe("PEM", function(){
- var privateKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n"+
+ describe("Imprt/Export keys", function(){
+ var privateKeyPKCS1 = "-----BEGIN RSA PRIVATE KEY-----\n"+
"MIIFwgIBAAKCAUEAsE1edyfToZRv6cFOkB0tAJ5qJor4YF5CccJAL0fS/o1Yk10V\n"+
"SXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1CnWtRjtNEcIfycqrZrhu6you5syb6\n"+
"ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvlu7hbDhNLIYo1zKFb/aUBbD6+UcaG\n"+
@@ -194,7 +194,7 @@ describe("NodeRSA", function(){
"RywhDUAjW8U0RBnzlmXhQQ6B9bjqooS2MsRrJrS5CU682fb3hBo=\n"+
"-----END RSA PRIVATE KEY-----";
- var publicKeyPEM = "-----BEGIN PUBLIC KEY-----\n"+
+ var publicKeyPKCS8 = "-----BEGIN PUBLIC KEY-----\n"+
"MIIBYjANBgkqhkiG9w0BAQEFAAOCAU8AMIIBSgKCAUEAsE1edyfToZRv6cFOkB0t\n"+
"AJ5qJor4YF5CccJAL0fS/o1Yk10VSXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1C\n"+
"nWtRjtNEcIfycqrZrhu6you5syb6ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvl\n"+
@@ -205,11 +205,10 @@ describe("NodeRSA", function(){
"KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n"+
"-----END PUBLIC KEY-----";
- var privateKeyPEMNotTrimmed = ' \n\n \n\n ' + privateKeyPEM + '\n \n \n\n ';
- var publicKeyPEMNotTrimmed = '\n\n\n\n ' + publicKeyPEM + '\n \n\n\n ';
+ var privateKeyPEMNotTrimmed = ' \n\n \n\n ' + privateKeyPKCS1 + '\n \n \n\n ';
+ var publicKeyPEMNotTrimmed = '\n\n\n\n ' + publicKeyPKCS8 + '\n \n\n\n ';
- var fileKey = __dirname + "/private.key";
- var fileKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n"+
+ var fileKeyPKCS1 = "-----BEGIN RSA PRIVATE KEY-----\n"+
"MIICXAIBAAKBgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmP\n"+
"GbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVey\n"+
"Syv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQIDAQAB\n"+
@@ -224,64 +223,116 @@ describe("NodeRSA", function(){
"q3V0cqHb6c8rI4TizRsCQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL\n"+
"icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6o=\n"+
"-----END RSA PRIVATE KEY-----";
+ var keysFolder = __dirname + '/keys/';
+ var keys_formats = {
+ 'pkcs1-private-der': {public: false, der: true, file: 'private_pkcs1.der'},
+ 'pkcs1-private-pem': {public: false, der: false, file: 'private_pkcs1.pem'},
+ 'pkcs8-private-der': {public: false, der: true, file: 'private_pkcs8.der'},
+ 'pkcs8-private-pem': {public: false, der: false, file: 'private_pkcs8.pem'},
+ 'pkcs1-public-der': {public: true, der: true, file: 'public_pkcs1.der'},
+ 'pkcs1-public-pem': {public: true, der: false, file: 'public_pkcs1.pem'},
+ 'pkcs8-public-der': {public: true, der: true, file: 'public_pkcs8.der'},
+ 'pkcs8-public-pem': {public: true, der: false, file: 'public_pkcs8.pem'},
+
+ 'private': {public: false, der: false, file: 'private_pkcs1.pem'},
+ 'public': {public: true, der: false, file: 'public_pkcs8.pem'},
+ 'private-der': {public: false, der: true, file: 'private_pkcs1.der'},
+ 'public-der': {public: true, der: true, file: 'public_pkcs8.der'},
+
+ 'pkcs1': {public: false, der: false, file: 'private_pkcs1.pem'},
+ 'pkcs1-private': {public: false, der: false, file: 'private_pkcs1.pem'},
+ 'pkcs1-der': {public: false, der: true, file: 'private_pkcs1.der'},
+ 'pkcs8': {public: false, der: false, file: 'private_pkcs8.pem'},
+ 'pkcs8-private': {public: false, der: false, file: 'private_pkcs8.pem'},
+ 'pkcs8-der': {public: false, der: true, file: 'private_pkcs8.der'},
+ 'pkcs1-public': {public: true, der: false, file: 'public_pkcs1.pem'},
+ 'pkcs8-public': {public: true, der: false, file: 'public_pkcs8.pem'}
+ };
describe("Good cases", function () {
- it(".loadFromPrivatePEM() should load private key from (not trimmed) PEM string", function(){
- privateNodeRSA = new NodeRSA(privateKeyPEMNotTrimmed);
- assert.instanceOf(privateNodeRSA.keyPair, Object);
- assert(privateNodeRSA.isPrivate());
- assert(privateNodeRSA.isPublic());
- assert(!privateNodeRSA.isPublic(true));
- });
+ describe("Common cases", function () {
+ it("should load private key from (not trimmed) PKCS1-PEM string", function () {
+ privateNodeRSA = new NodeRSA(privateKeyPEMNotTrimmed);
+ assert.instanceOf(privateNodeRSA.keyPair, Object);
+ assert(privateNodeRSA.isPrivate());
+ assert(privateNodeRSA.isPublic());
+ assert(!privateNodeRSA.isPublic(true));
+ });
- it(".loadFromPublicPEM() should load public key from (not trimmed) PEM string", function(){
- publicNodeRSA = new NodeRSA(publicKeyPEMNotTrimmed);
- assert.instanceOf(privateNodeRSA.keyPair, Object);
- assert(publicNodeRSA.isPublic());
- assert(publicNodeRSA.isPublic(true));
- assert(!publicNodeRSA.isPrivate());
- });
+ it("should load public key from (not trimmed) PKCS8-PEM string", function () {
+ publicNodeRSA = new NodeRSA(publicKeyPEMNotTrimmed);
+ assert.instanceOf(privateNodeRSA.keyPair, Object);
+ assert(publicNodeRSA.isPublic());
+ assert(publicNodeRSA.isPublic(true));
+ assert(!publicNodeRSA.isPrivate());
+ });
- it(".exportPrivate() should return private PEM string", function(){
- assert.equal(privateNodeRSA.exportPrivate(), privateKeyPEM);
- });
+ it(".exportKey() should return private PEM string", function () {
+ assert.equal(privateNodeRSA.exportKey('private'), privateKeyPKCS1);
+ assert.equal(privateNodeRSA.exportKey(), privateKeyPKCS1);
+ });
- it(".exportPublic() from public key should return public PEM string", function(){
- assert.equal(publicNodeRSA.exportPublic(), publicKeyPEM);
- });
+ it(".exportKey() from public key should return pkcs8 public PEM string", function () {
+ assert.equal(publicNodeRSA.exportKey('public'), publicKeyPKCS8);
+ });
- it(".exportPublic() from private key should return public PEM string", function(){
- assert.equal(privateNodeRSA.exportPublic(), publicKeyPEM);
- });
+ it(".exportKey() from private key should return pkcs8 public PEM string", function () {
+ assert.equal(privateNodeRSA.exportKey('public'), publicKeyPKCS8);
+ });
- it("should create key from buffer/fs.readFileSync output", function(){
- var key = new NodeRSA(fs.readFileSync(fileKey));
- assert.equal(key.exportPrivate(), fileKeyPEM);
- key = new NodeRSA();
- key.importKey(fs.readFileSync(fileKey));
- assert.equal(key.exportPrivate(), fileKeyPEM);
+ it("should create and load key from buffer/fs.readFileSync output", function () {
+ var key = new NodeRSA(fs.readFileSync(keysFolder + 'private_pkcs1.pem'));
+ assert.equal(key.exportKey(), fileKeyPKCS1);
+ key = new NodeRSA();
+ key.importKey(fs.readFileSync(keysFolder + 'private_pkcs1.pem'));
+ assert.equal(key.exportKey(), fileKeyPKCS1);
+ });
});
- it("should load PEM from buffer/fs.readFileSync output", function(){
- var key = new NodeRSA();
- assert.equal(key.isEmpty(), true);
- key.importKey(fs.readFileSync(fileKey));
- assert.equal(key.isEmpty(), false);
- assert.equal(key.exportPrivate(), fileKeyPEM);
+ describe("Different key formats", function () {
+ var sampleKey = new NodeRSA(fileKeyPKCS1);
+
+ for(var format in keys_formats) {
+ (function(format) {
+ var options = keys_formats[format];
+
+ it("should load from " + options.file + " (" + format + ")", function () {
+ var key = new NodeRSA(fs.readFileSync(keysFolder + options.file), format);
+ if (options.public) {
+ assert.equal(key.exportKey('public'), sampleKey.exportKey('public'));
+ } else {
+ assert.equal(key.exportKey(), sampleKey.exportKey());
+ }
+ });
+
+ it("should export to \"" + format + "\" format", function () {
+ var keyData = fs.readFileSync(keysFolder + options.file);
+ var exported = sampleKey.exportKey(format);
+
+ if (options.der) {
+ assert(Buffer.isBuffer(exported));
+ assert.equal(exported.toString('hex'), keyData.toString('hex'));
+ } else {
+ assert(_.isString(exported));
+ assert.equal(exported.replace(/\s+|\n\r|\n|\r$/gm, ''), keyData.toString('utf8').replace(/\s+|\n\r|\n|\r$/gm, ''));
+ }
+ });
+ })(format);
+ }
});
});
describe("Bad cases", function () {
it("not public key", function(){
var key = new NodeRSA();
- assert.throw(function(){ key.exportPrivate(); }, Error, "It is not private key");
- assert.throw(function(){ key.exportPublic(); }, Error, "It is not public key");
+ assert.throw(function(){ key.exportKey(); }, Error, "It is not private key");
+ assert.throw(function(){ key.exportKey('public'); }, Error, "It is not public key");
});
it("not private key", function(){
- var key = new NodeRSA(publicKeyPEM);
- assert.throw(function(){ key.exportPrivate(); }, Error, "It is not private key");
- assert.doesNotThrow(function(){ key.exportPublic(); }, Error, "It is not public key");
+ var key = new NodeRSA(publicKeyPKCS8);
+ assert.throw(function(){ key.exportKey(); }, Error, "It is not private key");
+ assert.doesNotThrow(function(){ key.exportKey('public'); }, Error, "It is not public key");
});
});
});
@@ -355,7 +406,7 @@ describe("NodeRSA", function(){
(function (i) {
var suit = dataBundle[i];
it("should sign " + i, function () {
- key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportPrivate(), {
+ key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportKey(), {
signingScheme: scheme + '-sha256',
environment: env
});
@@ -376,7 +427,7 @@ describe("NodeRSA", function(){
for (var alg in signHashAlgorithms[env]) {
(function (alg) {
it("signing with custom algorithm (" + alg + ")", function () {
- var key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportPrivate(), {
+ var key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: env
});
@@ -392,7 +443,7 @@ describe("NodeRSA", function(){
describe("Bad cases" + (envs.length > 1 ? " in " + env + " environment" : ""), function () {
it("incorrect data for verifying", function () {
- var key = new NodeRSA(generatedKeys[0].exportPrivate(), {
+ var key = new NodeRSA(generatedKeys[0].exportKey(), {
signingScheme: scheme + '-sha256',
environment: env
});
@@ -401,7 +452,7 @@ describe("NodeRSA", function(){
});
it("incorrect key for signing", function () {
- var key = new NodeRSA(generatedKeys[0].exportPublic(), {
+ var key = new NodeRSA(generatedKeys[0].exportKey('pkcs8-public'), {
signingScheme: scheme + '-sha256',
environment: env
});
@@ -411,11 +462,11 @@ describe("NodeRSA", function(){
});
it("incorrect key for verifying", function () {
- var key1 = new NodeRSA(generatedKeys[0].exportPrivate(), {
+ var key1 = new NodeRSA(generatedKeys[0].exportKey(), {
signingScheme: scheme + '-sha256',
environment: env
});
- var key2 = new NodeRSA(generatedKeys[1].exportPublic(), {
+ var key2 = new NodeRSA(generatedKeys[1].exportKey('pkcs8-public'), {
signingScheme: scheme + '-sha256',
environment: env
});
@@ -432,11 +483,11 @@ describe("NodeRSA", function(){
});
it("different algorithms", function () {
- var singKey = new NodeRSA(generatedKeys[0].exportPrivate(), {
+ var singKey = new NodeRSA(generatedKeys[0].exportKey(), {
signingScheme: scheme + '-md5',
environment: env
});
- var verifyKey = new NodeRSA(generatedKeys[0].exportPrivate(), {
+ var verifyKey = new NodeRSA(generatedKeys[0].exportKey(), {
signingScheme: scheme + '-sha1',
environment: env
});
@@ -455,11 +506,11 @@ describe("NodeRSA", function(){
for (var alg in signHashAlgorithms['browser']) {
(function (alg) {
it("signing with custom algorithm (" + alg + ") (equal test)", function () {
- var nodeKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'node'
});
- var browserKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'browser'
});
@@ -468,11 +519,11 @@ describe("NodeRSA", function(){
});
it("sign in node & verify in browser (" + alg + ")", function () {
- var nodeKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'node'
});
- var browserKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'browser'
});
@@ -481,11 +532,11 @@ describe("NodeRSA", function(){
});
it("sign in browser & verify in node (" + alg + ")", function () {
- var nodeKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'node'
});
- var browserKey = new NodeRSA(generatedKeys[5].exportPrivate(), {
+ var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
signingScheme: scheme + '-' + alg,
environment: 'browser'
});