forked from daren.hsu/line_push
158 lines
7.5 KiB
Markdown
158 lines
7.5 KiB
Markdown
Universal Module for Cryptographic Key Utilities in JavaScript
|
|
--
|
|
[](https://badge.fury.io/js/js-crypto-key-utils)
|
|
[](https://david-dm.org/junkurihara/jscu?path=packages/js-crypto-key-utils)
|
|
[](https://opensource.org/licenses/MIT)
|
|
|
|
|
|
> **WARNING**: At this time this solution should be considered suitable for research and experimentation, further code and security review is needed before utilization in a production application.
|
|
|
|
# Introduction and Overview
|
|
This library is designed to 'universally' provide several functions for a cryptographic key handling, which means it works both on most browsers and on Node.js just by importing from npm/source code. This key utility library provides converters for EC/RSA keys in PEM/DER<->JWK, octet form of EC keys <-> JWK, and computation of JWK thumbprints. Especially for the conversion PEM/DER <->JWK, encryption and decryption of private key in DER/PEM are supported.
|
|
|
|
# Installation
|
|
At your project directory, do either one of the following.
|
|
|
|
- From npm/yarn:
|
|
```shell
|
|
$ npm install --save js-crypto-key-utils // npm
|
|
$ yarn add js-crypto-key-utils // yarn
|
|
```
|
|
- From GitHub:
|
|
```shell
|
|
$ git clone https://github.com/junkurihara/jscu.git
|
|
$ cd js-crypto-utils/packages/js-crypto-key-utils
|
|
& yarn build
|
|
```
|
|
|
|
Then you should import the package as follows.
|
|
|
|
```shell
|
|
import keyutil from 'js-crypto-key-utils'; // for npm
|
|
import keyutil from 'path/to/js-crypto-key-utils/dist/index.js'; // for github
|
|
```
|
|
|
|
The bundled file is also given as `js-crypto-key-utils/dist/jsckey.bundle.js` for a use case where the module is imported as a `window.jsckey` object via `script` tags.
|
|
|
|
|
|
# Usage
|
|
Supported key types are Json Web Key (JWK, [RFC7517](https://tools.ietf.org/html/rfc7517)), and PEM/DER. Octet-Formatted Key ([SECG SEC1](http://www.secg.org/sec1-v2.pdf) 2.3.3 and 2.3.4, link to PDF) is also available for elliptic curve cryptography keys. Note that for PEM/DER, public keys are encoded to the form of `SubjectPublicKeyInfo` (SPKI) defined as a part of X.509 public key certificate ([RFC5280](https://tools.ietf.org/html/rfc5280)). The detailed encoding rule for elliptic curve cryptographic keys is given in [RFC5480](https://tools.ietf.org/html/rfc5480). On the other hand, private keys are encoded to hte form of `PrivateKeyInfo` defined in PKCS#8 ([RFC5958](https://tools.ietf.org/html/rfc5958)). The detailed encoding rule for elliptic curve cryptographic keys is given in [RFC5915](https://tools.ietf.org/html/rfc5915) as well as SPKI. Please refer to [RFC3447](https://tools.ietf.org/html/rfc3447) for the detailed encoding rule of RSA public and private keys.
|
|
|
|
## Instantiation
|
|
At first, you need to instantiate `Key` object by importing various type of keys.
|
|
|
|
```javascript
|
|
const yourStringPemKey = '------BEGIN PRIVATE...'; // SPKI (public key) or PKCS8 (either encrypted or plaintext private key)
|
|
const yourBinaryDerKey = new Uint8Array([...]); // SPKI (public key) or PKCS8 (either encrypted or plaintext private key)
|
|
const yourJasonWebKey = {kty: 'EC', ... };
|
|
const yourOctetFormKey = new Uint8Array([0x04, ...]) // only for Elliptic Curve Crypto Key.
|
|
|
|
const keyObjFromPem = new keyutil.Key('pem', yourStringPemKey);
|
|
const keyObjFromDer = new keyutil.Key('der', yourBinaryDerKey);
|
|
const keyObjFromJwk = new keyutil.Key('jwk', yourJasonWebKey);
|
|
const keyObjFromOct = new keyutil.Key('oct', yourOctetFormKey, {namedCurve: '...'}); //namedCurve like 'P-256K' is required.
|
|
```
|
|
|
|
## Handling key objects
|
|
In a case where the imported key is encrypted (pem/der), it needs to be decrypted before getting exported.
|
|
```javascript
|
|
const keyObj = new keyutil.Key('pem', encryptedPemKey);
|
|
const yourPassphrase = '...';
|
|
|
|
// first now you can check the status
|
|
const isEncrypted = keyObj.isEncrypted;
|
|
|
|
// before using the key object, decrypt the key object with the passphrase.
|
|
if(isEncrypted) await keyObj.decrypt(yourPassphrase);
|
|
|
|
const thisMustBeFalse = keyObj.isEncrypted; // false
|
|
|
|
// you can lock the key object by encrypting it as well.
|
|
if(!thisMustBeFalse) await keyObj.encrypt(yourPassphrase);
|
|
|
|
const thisMustBeTrue = keyObj.isEncrypted; // true;
|
|
```
|
|
|
|
## Exporting keys in desired format
|
|
From instantiated key objects, various types of keys can be exported.
|
|
```javascript
|
|
const keyObj = new keyutil.Key('pem', pemKey);
|
|
|
|
// jwk
|
|
let jwk;
|
|
if(!keyObj.isEncrypted) jwk = await keyObj.export('jwk');
|
|
if(keyObj.isPrivate) jwk = await keyObj.export('jwk', {outputPublic: true}); // export public key from private key.
|
|
|
|
// pem
|
|
let pem;
|
|
if(!keyObj.isEncrypted) pem = await keyObj.export('pem');
|
|
if(keyObj.isPrivate) pem = await keyObj.export('pem', {outputPublic: true}); // export public key from private key.
|
|
// Only ECC Key: export compressed form of public key from private/public key.
|
|
pem = await keyObj.export('pem', {outputPublic: true, compact: true});
|
|
|
|
// der
|
|
let der;
|
|
if(!keyObj.isEncrypted) der = await keyObj.export('der');
|
|
if(keyObj.isPrivate) der = await keyObj.export('der', {outputPublic: true}); // export public key from private key.
|
|
// Only ECC Keys: export compressed form of public key from private/public key.
|
|
der = await keyObj.export('der', {outputPublic: true, compact: true});
|
|
|
|
// Only ECC Keys
|
|
// octet from
|
|
let oct;
|
|
if(!keyObj.isEncrypted) oct = await keyObj.export('oct');
|
|
if(keyObj.isPrivate) oct = await keyObj.export('oct', {outputPublic: true}); // export public key from private key.
|
|
// export compressed form of public key from private/public key.
|
|
oct = await keyObj.export('oct', {outputPublic: true, compact: true});
|
|
```
|
|
|
|
|
|
## Exporting encrypted keys with arbitrary specified passphrase.
|
|
All you need to export encrypted private keys in PEM/DER is just putting passphrase in the API. The default encryption algorithm follows PKCS#5 v2.1 ([RFC8018](https://tools.ietf.org/html/rfc8018)) and uses AES256-CBC and HMAC-with-SHA-256 to encrypt your private key.
|
|
```javascript
|
|
const keyObj = new keyutil.Key('pem', pemKey);
|
|
|
|
// encrypt with default params
|
|
let encryptedPem;
|
|
if(!keyObj.isEncrypted && keyObj.isPrivate)
|
|
encryptedPem = await keyObj.export('pem', {encryptParams: {passphrase: 'top secret'}});
|
|
|
|
// encrypt with intended params
|
|
if(!keyObj.isEncrypted && keyObj.isPrivate)
|
|
encryptedPem = await keyObj.export('pem', {
|
|
encryptParams: {
|
|
passphrase: 'top secret',
|
|
algorithm: 'pbes2', // default. 'pbeWith...' is also available, i.e., pbes1.
|
|
cipher: 'aes256-cbc', // default. for encryption. 'aes128-cbc', 'aes192-cbc'(only node), 'des-ede3-cbc' are available as well.
|
|
prf: 'hmacWithSHA256' // default. for key derivation
|
|
}
|
|
});
|
|
```
|
|
|
|
## Getting JWK Thumbprint
|
|
You can obtain the JWK Thumbprint defined in [RFC7638](https://tools.ietf.org/html/rfc7638) from instantiated key object. The API can be invoked as follows.
|
|
```javascript
|
|
const keyObj = new keyutil.Key('pem', pemKey);
|
|
|
|
let thumbprint;
|
|
|
|
// with default params (hash is SHA-256, output is in Uint8Array)
|
|
if(!keyObj.isEncrypted) thumbprint = await keyObj.getJwkThumbprint();
|
|
|
|
// with intented params
|
|
if(!keyObj.isEncrypted) thumbprint = await keyObj.getJwkThumbprint(
|
|
'SHA-512',
|
|
'hex' // output is hex string
|
|
);
|
|
```
|
|
Note that the thumbprint generated from a public key is exactly same as that from its paired private key.
|
|
|
|
# Note
|
|
Now this library supports the following curve for elliptic curve cryptography.
|
|
- P-256 (secp256r1)
|
|
- P-384 (secp384r1)
|
|
- P-521 (secp521r1)
|
|
- P-256K (secp256k1)
|
|
|
|
# License
|
|
Licensed under the MIT license, see `LICENSE` file. |