From c1627a99e74d0806e1b3303f744e980856333ac4 Mon Sep 17 00:00:00 2001 From: Richard Schneider Date: Thu, 21 Dec 2017 02:43:54 +1300 Subject: [PATCH] feat: use libp2p-crypto (#18) * test: openssl interop is now the responsibility of libp2p-crypto * feat: use libp2p-crypto, not node-forge, for key management * fix: use libp2p-crypto.pbkdf, not node-forge * fix: do not ship CMS This removes all depencies on node-forge * test: update dependencies * test: remove dead code --- README.md | 7 +- package.json | 12 ++- src/cms.js | 96 --------------------- src/keychain.js | 190 ++++++++++++++++-------------------------- src/util.js | 86 ------------------- test/browser.js | 1 - test/keychain.spec.js | 65 +++------------ test/node.js | 1 - test/openssl.js | 154 ---------------------------------- 9 files changed, 90 insertions(+), 522 deletions(-) delete mode 100644 src/cms.js delete mode 100644 src/util.js delete mode 100644 test/openssl.js diff --git a/README.md b/README.md index 5d90826f..a4a0a0ac 100644 --- a/README.md +++ b/README.md @@ -85,15 +85,14 @@ The **key id** is the SHA-256 [multihash](https://github.com/multiformats/multih A private key is stored as an encrypted PKCS 8 structure in the PEM format. It is protected by a key generated from the key chain's *passPhrase* using **PBKDF2**. -The default options for generating the derived encryption key are in the `dek` object +The default options for generating the derived encryption key are in the `dek` object. This, along with the passPhrase, is the input to a `PBKDF2` function. + ```js const defaultOptions = { - createIfNeeded: true, - //See https://cryptosense.com/parameter-choice-for-pbkdf2/ dek: { keyLength: 512 / 8, - iterationCount: 10000, + iterationCount: 1000, salt: 'at least 16 characters long', hash: 'sha2-512' } diff --git a/package.json b/package.json index f90524fe..541f3080 100644 --- a/package.json +++ b/package.json @@ -45,22 +45,20 @@ "async": "^2.6.0", "deepmerge": "^1.5.2", "interface-datastore": "~0.4.1", - "libp2p-crypto": "~0.10.3", - "multihashes": "~0.4.12", - "node-forge": "~0.7.1", + "libp2p-crypto": "~0.11.0", "pull-stream": "^3.6.1", "sanitize-filename": "^1.6.1" }, "devDependencies": { - "aegir": "^12.2.0", + "aegir": "^12.3.0", "chai": "^4.1.2", "chai-string": "^1.4.0", - "datastore-fs": "^0.4.1", - "datastore-level": "^0.7.0", + "datastore-fs": "~0.4.1", + "datastore-level": "~0.7.0", "dirty-chai": "^2.0.1", "level-js": "^2.2.4", "mocha": "^4.0.1", - "peer-id": "^0.10.2", + "peer-id": "~0.10.4", "pre-commit": "^1.2.2", "rimraf": "^2.6.2" } diff --git a/src/cms.js b/src/cms.js deleted file mode 100644 index fb66c2df..00000000 --- a/src/cms.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict' - -const async = require('async') -const forge = require('node-forge') -const util = require('./util') - -class CMS { - constructor (keystore) { - if (!keystore) { - throw new Error('keystore is required') - } - - this.keystore = keystore - } - - createAnonymousEncryptedData (name, plain, callback) { - const self = this - if (!Buffer.isBuffer(plain)) { - return callback(new Error('Data is required')) - } - - self.keystore._getPrivateKey(name, (err, key) => { - if (err) { - return callback(err) - } - - try { - const privateKey = forge.pki.decryptRsaPrivateKey(key, self.keystore._()) - util.certificateForKey(privateKey, (err, certificate) => { - if (err) return callback(err) - - // create a p7 enveloped message - const p7 = forge.pkcs7.createEnvelopedData() - p7.addRecipient(certificate) - p7.content = forge.util.createBuffer(plain) - p7.encrypt() - - // convert message to DER - const der = forge.asn1.toDer(p7.toAsn1()).getBytes() - callback(null, Buffer.from(der, 'binary')) - }) - } catch (err) { - callback(err) - } - }) - } - - readData (cmsData, callback) { - if (!Buffer.isBuffer(cmsData)) { - return callback(new Error('CMS data is required')) - } - - const self = this - let cms - try { - const buf = forge.util.createBuffer(cmsData.toString('binary')) - const obj = forge.asn1.fromDer(buf) - cms = forge.pkcs7.messageFromAsn1(obj) - } catch (err) { - return callback(new Error('Invalid CMS: ' + err.message)) - } - - // Find a recipient whose key we hold. We only deal with recipient certs - // issued by ipfs (O=ipfs). - const recipients = cms.recipients - .filter(r => r.issuer.find(a => a.shortName === 'O' && a.value === 'ipfs')) - .filter(r => r.issuer.find(a => a.shortName === 'CN')) - .map(r => { - return { - recipient: r, - keyId: r.issuer.find(a => a.shortName === 'CN').value - } - }) - async.detect( - recipients, - (r, cb) => self.keystore.findKeyById(r.keyId, (err, info) => cb(null, !err && info)), - (err, r) => { - if (err) return callback(err) - if (!r) return callback(new Error('No key found for decryption')) - - async.waterfall([ - (cb) => self.keystore.findKeyById(r.keyId, cb), - (key, cb) => self.keystore._getPrivateKey(key.name, cb) - ], (err, pem) => { - if (err) return callback(err) - - const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keystore._()) - cms.decrypt(r.recipient, privateKey) - async.setImmediate(() => callback(null, Buffer.from(cms.content.getBytes(), 'binary'))) - }) - } - ) - } -} - -module.exports = CMS diff --git a/src/keychain.js b/src/keychain.js index 004174d6..3d20504f 100644 --- a/src/keychain.js +++ b/src/keychain.js @@ -1,11 +1,9 @@ +/* eslint max-nested-callbacks: ["error", 5] */ 'use strict' const sanitize = require('sanitize-filename') -const forge = require('node-forge') const deepmerge = require('deepmerge') const crypto = require('libp2p-crypto') -const util = require('./util') -const CMS = require('./cms') const DS = require('interface-datastore') const pull = require('pull-stream') @@ -19,24 +17,11 @@ const NIST = { minIterationCount: 1000 } -/** - * Maps an IPFS hash name to its forge equivalent. - * - * See https://github.com/multiformats/multihash/blob/master/hashtable.csv - * - * @private - */ -const hashName2Forge = { - sha1: 'sha1', - 'sha2-256': 'sha256', - 'sha2-512': 'sha512' -} - const defaultOptions = { // See https://cryptosense.com/parametesr-choice-for-pbkdf2/ dek: { keyLength: 512 / 8, - iterationCount: 10000, + iterationCount: 1000, salt: 'you should override this value with a crypto secure random number', hash: 'sha2-512' } @@ -133,26 +118,15 @@ class Keychain { if (opts.dek.iterationCount < NIST.minIterationCount) { throw new Error(`dek.iterationCount must be least ${NIST.minIterationCount}`) } - this.dek = opts.dek - - // Get the hashing alogorithm - const hashAlgorithm = hashName2Forge[opts.dek.hash] - if (!hashAlgorithm) { - throw new Error(`dek.hash '${opts.dek.hash}' is unknown or not supported`) - } // Create the derived encrypting key - let dek = forge.pkcs5.pbkdf2( + const dek = crypto.pbkdf2( opts.passPhrase, opts.dek.salt, opts.dek.iterationCount, opts.dek.keyLength, - hashAlgorithm) - dek = forge.util.bytesToHex(dek) + opts.dek.hash) Object.defineProperty(this, '_', { value: () => dek }) - - // Provide access to protected messages - this.cms = new CMS(this) } /** @@ -189,31 +163,32 @@ class Keychain { if (size < 2048) { return _error(callback, `Invalid RSA key size ${size}`) } - forge.pki.rsa.generateKeyPair({bits: size, workers: -1}, (err, keypair) => { + break + default: + break + } + + crypto.keys.generateKeyPair(type, size, (err, keypair) => { + if (err) return _error(callback, err) + keypair.id((err, kid) => { + if (err) return _error(callback, err) + keypair.export(this._(), (err, pem) => { if (err) return _error(callback, err) - util.keyId(keypair.privateKey, (err, kid) => { + const keyInfo = { + name: name, + id: kid + } + const batch = self.store.batch() + batch.put(dsname, pem) + batch.put(DsInfoName(name), JSON.stringify(keyInfo)) + batch.commit((err) => { if (err) return _error(callback, err) - const pem = forge.pki.encryptRsaPrivateKey(keypair.privateKey, this._()) - const keyInfo = { - name: name, - id: kid - } - const batch = self.store.batch() - batch.put(dsname, pem) - batch.put(DsInfoName(name), JSON.stringify(keyInfo)) - batch.commit((err) => { - if (err) return _error(callback, err) - - callback(null, keyInfo) - }) + callback(null, keyInfo) }) }) - break - - default: - return _error(callback, `Invalid key type '${type}'`) - } + }) + }) }) } @@ -372,19 +347,10 @@ class Keychain { return _error(callback, `Key '${name}' does not exist. ${err.message}`) } const pem = res.toString() - try { - const options = { - algorithm: 'aes256', - count: this.dek.iterationCount, - saltSize: NIST.minSaltLength, - prfAlgorithm: 'sha512' - } - const privateKey = forge.pki.decryptRsaPrivateKey(pem, this._()) - const res = forge.pki.encryptRsaPrivateKey(privateKey, password, options) - return callback(null, res) - } catch (e) { - _error(callback, e) - } + crypto.keys.import(pem, this._(), (err, privateKey) => { + if (err) return _error(callback, err) + privateKey.export(password, callback) + }) }) } @@ -409,62 +375,12 @@ class Keychain { self.store.has(dsname, (err, exists) => { if (err) return _error(callback, err) if (exists) return _error(callback, `Key '${name}' already exists`) - try { - const privateKey = forge.pki.decryptRsaPrivateKey(pem, password) - if (privateKey === null) { - return _error(callback, 'Cannot read the key, most likely the password is wrong') - } - const newpem = forge.pki.encryptRsaPrivateKey(privateKey, this._()) - util.keyId(privateKey, (err, kid) => { + crypto.keys.import(pem, password, (err, privateKey) => { + if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong') + privateKey.id((err, kid) => { if (err) return _error(callback, err) - - const keyInfo = { - name: name, - id: kid - } - const batch = self.store.batch() - batch.put(dsname, newpem) - batch.put(DsInfoName(name), JSON.stringify(keyInfo)) - batch.commit((err) => { + privateKey.export(this._(), (err, pem) => { if (err) return _error(callback, err) - - callback(null, keyInfo) - }) - }) - } catch (err) { - _error(callback, err) - } - }) - } - - importPeer (name, peer, callback) { - const self = this - if (!validateKeyName(name)) { - return _error(callback, `Invalid key name '${name}'`) - } - if (!peer || !peer.privKey) { - return _error(callback, 'Peer.privKey is required') - } - const dsname = DsName(name) - self.store.has(dsname, (err, exists) => { - if (err) return _error(callback, err) - if (exists) return _error(callback, `Key '${name}' already exists`) - - const privateKeyProtobuf = peer.marshalPrivKey() - crypto.keys.unmarshalPrivateKey(privateKeyProtobuf, (err, key) => { - if (err) return _error(callback, err) - try { - const der = key.marshal() - const buf = forge.util.createBuffer(der.toString('binary')) - const obj = forge.asn1.fromDer(buf) - const privateKey = forge.pki.privateKeyFromAsn1(obj) - if (privateKey === null) { - return _error(callback, 'Cannot read the peer private key') - } - const pem = forge.pki.encryptRsaPrivateKey(privateKey, this._()) - util.keyId(privateKey, (err, kid) => { - if (err) return _error(callback, err) - const keyInfo = { name: name, id: kid @@ -478,9 +394,43 @@ class Keychain { callback(null, keyInfo) }) }) - } catch (err) { - _error(callback, err) - } + }) + }) + }) + } + + importPeer (name, peer, callback) { + const self = this + if (!validateKeyName(name)) { + return _error(callback, `Invalid key name '${name}'`) + } + if (!peer || !peer.privKey) { + return _error(callback, 'Peer.privKey is required') + } + + const privateKey = peer.privKey + const dsname = DsName(name) + self.store.has(dsname, (err, exists) => { + if (err) return _error(callback, err) + if (exists) return _error(callback, `Key '${name}' already exists`) + + privateKey.id((err, kid) => { + if (err) return _error(callback, err) + privateKey.export(this._(), (err, pem) => { + if (err) return _error(callback, err) + const keyInfo = { + name: name, + id: kid + } + const batch = self.store.batch() + batch.put(dsname, pem) + batch.put(DsInfoName(name), JSON.stringify(keyInfo)) + batch.commit((err) => { + if (err) return _error(callback, err) + + callback(null, keyInfo) + }) + }) }) }) } diff --git a/src/util.js b/src/util.js deleted file mode 100644 index 6066c33f..00000000 --- a/src/util.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict' - -const forge = require('node-forge') -const pki = forge.pki -const multihash = require('multihashes') -const rsaUtils = require('libp2p-crypto/src/keys/rsa-utils') -const rsaClass = require('libp2p-crypto/src/keys/rsa-class') - -exports = module.exports - -// Create an IPFS key id; the SHA-256 multihash of a public key. -// See https://github.com/richardschneider/ipfs-encryption/issues/16 -exports.keyId = (privateKey, callback) => { - try { - const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e) - const spki = pki.publicKeyToSubjectPublicKeyInfo(publicKey) - const der = Buffer.from(forge.asn1.toDer(spki).getBytes(), 'binary') - const jwk = rsaUtils.pkixToJwk(der) - const rsa = new rsaClass.RsaPublicKey(jwk) - rsa.hash((err, kid) => { - if (err) return callback(err) - - const kids = multihash.toB58String(kid) - return callback(null, kids) - }) - } catch (err) { - callback(err) - } -} - -exports.certificateForKey = (privateKey, callback) => { - exports.keyId(privateKey, (err, kid) => { - if (err) return callback(err) - - const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e) - const cert = pki.createCertificate() - cert.publicKey = publicKey - cert.serialNumber = '01' - cert.validity.notBefore = new Date() - cert.validity.notAfter = new Date() - cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10) - var attrs = [{ - name: 'organizationName', - value: 'ipfs' - }, { - shortName: 'OU', - value: 'keystore' - }, { - name: 'commonName', - value: kid - }] - cert.setSubject(attrs) - cert.setIssuer(attrs) - cert.setExtensions([{ - name: 'basicConstraints', - cA: true - }, { - name: 'keyUsage', - keyCertSign: true, - digitalSignature: true, - nonRepudiation: true, - keyEncipherment: true, - dataEncipherment: true - }, { - name: 'extKeyUsage', - serverAuth: true, - clientAuth: true, - codeSigning: true, - emailProtection: true, - timeStamping: true - }, { - name: 'nsCertType', - client: true, - server: true, - email: true, - objsign: true, - sslCA: true, - emailCA: true, - objCA: true - }]) - // self-sign certificate - cert.sign(privateKey) - - return callback(null, cert) - }) -} diff --git a/test/browser.js b/test/browser.js index 9584a635..4e08b137 100644 --- a/test/browser.js +++ b/test/browser.js @@ -23,6 +23,5 @@ describe('browser', () => { }) require('./keychain.spec')(datastore1, datastore2) - require('./openssl')(datastore1) require('./peerid') }) diff --git a/test/keychain.spec.js b/test/keychain.spec.js index 2c781e3f..aae21b17 100644 --- a/test/keychain.spec.js +++ b/test/keychain.spec.js @@ -16,11 +16,10 @@ module.exports = (datastore1, datastore2) => { const rsaKeyName = 'tajné jméno' const renamedRsaKeyName = 'ชื่อลับ' let rsaKeyInfo - let emptyKeystore + // let emptyKeystore let ks before((done) => { - emptyKeystore = new Keychain(datastore1, { passPhrase: passPhrase }) ks = new Keychain(datastore2, { passPhrase: passPhrase }) done() }) @@ -163,56 +162,6 @@ module.exports = (datastore1, datastore2) => { }) }) - describe('CMS protected data', () => { - const plainData = Buffer.from('This is a message from Alice to Bob') - let cms - - it('service is available', (done) => { - expect(ks).to.have.property('cms') - done() - }) - - it('is anonymous', (done) => { - ks.cms.createAnonymousEncryptedData(rsaKeyName, plainData, (err, msg) => { - expect(err).to.not.exist() - expect(msg).to.exist() - expect(msg).to.be.instanceOf(Buffer) - cms = msg - done() - }) - }) - - it('is a PKCS #7 message', (done) => { - ks.cms.readData('not CMS', (err) => { - expect(err).to.exist() - done() - }) - }) - - it('is a PKCS #7 binary message', (done) => { - ks.cms.readData(plainData, (err) => { - expect(err).to.exist() - done() - }) - }) - - it('cannot be read without the key', (done) => { - emptyKeystore.cms.readData(cms, (err, plain) => { - expect(err).to.exist() - done() - }) - }) - - it('can be read with the key', (done) => { - ks.cms.readData(cms, (err, plain) => { - expect(err).to.not.exist() - expect(plain).to.exist() - expect(plain.toString()).to.equal(plainData.toString()) - done() - }) - }) - }) - describe('exported key', () => { let pemKey @@ -272,7 +221,17 @@ module.exports = (datastore1, datastore2) => { }) }) - it('key exists', (done) => { + it('key id exists', (done) => { + ks.findKeyById(alice.toB58String(), (err, key) => { + expect(err).to.not.exist() + expect(key).to.exist() + expect(key).to.have.property('name', 'alice') + expect(key).to.have.property('id', alice.toB58String()) + done() + }) + }) + + it('key name exists', (done) => { ks.findKeyByName('alice', (err, key) => { expect(err).to.not.exist() expect(key).to.exist() diff --git a/test/node.js b/test/node.js index 634716cf..b003a7c8 100644 --- a/test/node.js +++ b/test/node.js @@ -30,6 +30,5 @@ describe('node', () => { }) require('./keychain.spec')(datastore1, datastore2) - require('./openssl')(datastore1) require('./peerid') }) diff --git a/test/openssl.js b/test/openssl.js deleted file mode 100644 index 4c8134dc..00000000 --- a/test/openssl.js +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint max-nested-callbacks: ["error", 8] */ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const Keychain = require('..') - -module.exports = (datastore1) => { - describe('interop with openssl', () => { - const passPhrase = 'this is not a secure phrase' - const keyName = 'openssl-key' - let ks - - before((done) => { - ks = new Keychain(datastore1, { passPhrase: passPhrase }) - done() - }) - - it('can read a private key', (done) => { - /* - * Generated with - * openssl genpkey -algorithm RSA - * -pkeyopt rsa_keygen_bits:3072 - * -pkeyopt rsa_keygen_pubexp:65537 - */ - const pem = `-----BEGIN PRIVATE KEY----- -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 -4pMwXeXP+LO8NIfRXV8mgrm86g== ------END PRIVATE KEY----- -` - ks.importKey(keyName, pem, '', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - expect(key).to.have.property('name', keyName) - expect(key).to.have.property('id') - ks.removeKey(keyName, done) - }) - }) - - // TODO: net.forge can not cope with this - // Uncaught AssertionError: expected [Error: Cannot read encrypted PBE data block. Unsupported OID.] to not exist - it.skip('can read a private encrypted key (v1)', (done) => { - /* - * Generated with - * openssl genpkey -algorithm RSA - * -pkeyopt rsa_keygen_bits:1024 - * -pkeyopt rsa_keygen_pubexp:65537 - * -out foo.pem - * openssl pkcs8 -in foo.pem -topk8 -passout pass:mypassword - */ - const pem = `-----BEGIN ENCRYPTED PRIVATE KEY----- -MIICoTAbBgkqhkiG9w0BBQMwDgQI2563Jugj/KkCAggABIICgPxHkKtUUE8EWevq -eX9nTjqpbsv0QoXQMhegfxDELJLU8tj6V0bWNt7QDdfQ1n6FRgnNvNGick6gyqHH -yH9qC2oXwkDFP7OrHp2NEZd7DHQLLc+L4KJ/0dzsiZ1U9no7XzQMUay9Bc918ADE -pN2/EqigWkaG4gNjkAeKWr6+BNRevDXlSvls7YDboNcTiACi5zJkthivB9g3vT1m -gPdN6Gf/mmqtBTDHeqj5QsmXYqeCyo5b26JgYsziABVZDHph4ekPUsTvudRpE9Ex -baXwdYEAZxVpSbTvQ3A5qysjSZeM9ttfRTSSwL391q7dViz4+aujpk0Vj7piH+1B -CkfO8/XudRdRlnOe+KjMidktKCsMGCIOW92IlfMvIQ/Zn1GTYj9bRXONFNJ2WPND -UmCKnL7cmworwg/weRorrGKBWIGspU+tDASOPSvIGKo6Hoxm4CN1TpDRY7DAGlgm -Y3TEbMYfpXyzkPjvAhJDt03D3J9PrTO6uM5d7YUaaTmJ2TQFQVF2Lc3Uz8lDJLs0 -ZYtfQ/4H+YY2RrX7ua7t6ArUcYXZtv0J4lRYWjwV8fGPUVc0d8xLJU0Yjf4BD7K8 -rsavHo9b5YvBUX7SgUyxAEembEOe3SjQ+gPu2U5wovcjUuC9eItEEsXGrx30BQ0E -8BtK2+hp0eMkW5/BYckJkH+Yl8ypbzRGRRIZzLgeI4JveSx/mNhewfgTr+ORPThZ -mBdkD5r+ixWF174naw53L8U9wF8kiK7pIE1N9TR4USEeovLwX6Ni/2MMDZedOfof -2f77eUdLsK19/5/lcgAAYaXauXWhy2d2r3SayFrC9woy0lh2VLKRMBjcx1oWb7dp -0uxzo5Y= ------END ENCRYPTED PRIVATE KEY----- -` - ks.importKey(keyName, pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - expect(key).to.have.property('name', keyName) - expect(key).to.have.property('id') - ks.removeKey(keyName, done) - }) - }) - - it('can read a private encrypted key (v2)', (done) => { - /* - * Generated with - * openssl genpkey -algorithm RSA - * -pkeyopt rsa_keygen_bits:1024 - * -pkeyopt rsa_keygen_pubexp:65537 - * -out foo.pem - * openssl pkcs8 -in foo.pem -topk8 -v2 aes-256-cbc -passout pass:mypassword - */ - const pem = `-----BEGIN ENCRYPTED PRIVATE KEY----- -MIICzzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIhuL894loRucCAggA -MB0GCWCGSAFlAwQBKgQQEoEtsjW3iC9/u0uGvkxX7wSCAoAsX3l6JoR2OGbT8CkY -YT3RQFqquOgItYOHw6E3tir2YrmxEAo99nxoL8pdto37KSC32eAGnfv5R1zmHHSx -0M3/y2AWiCBTX95EEzdtGC1hK3PBa/qpp/xEmcrsjYN6NXxMAkhC0hMP/HdvqMAg -ee7upvaYJsJcl8QLFNayAWr8b8cZA/RBhGEIRl59Eyj6nNtxDt3bCrfe06o1CPCV -50/fRZEwFOi/C6GYvPN6MrPZO3ALBWgopLT2yQqycTKtfxYWIdOsMBkAjKf2D6Pk -u2mqBsaP4b71jIIeT4euSJLsoJV+O39s8YHXtW8GtOqp7V5kIlnm90lZ9wzeLTZ7 -HJsD/jEdYto5J3YWm2wwEDccraffJSm7UDtJBvQdIx832kxeFCcGQjW38Zl1qqkg -iTH1PLTypxj2ZuviS2EkXVFb/kVU6leWwOt6fqWFC58UvJKeCk/6veazz3PDnTWM -92ClUqFd+CZn9VT4CIaJaAc6v5NLpPp+T9sRX9AtequPm7FyTeevY9bElfyk9gW9 -JDKgKxs6DGWDa16RL5vzwtU+G3o6w6IU+mEwa6/c+hN+pRFs/KBNLLSP9OHBx7BJ -X/32Ft+VFhJaK+lQ+f+hve7od/bgKnz4c/Vtp7Dh51DgWgCpBgb8p0vqu02vTnxD -BXtDv3h75l5PhvdWfVIzpMWRYFvPR+vJi066FjAz2sjYc0NMLSYtZWyWoIInjhoX -Dp5CQujCtw/ZSSlwde1DKEWAW4SeDZAOQNvuz0rU3eosNUJxEmh3aSrcrRtDpw+Y -mBUuWAZMpz7njBi7h+JDfmSW/GAaMwrVFC2gef5375R0TejAh+COAjItyoeYEvv8 -DQd8 ------END ENCRYPTED PRIVATE KEY----- -` - ks.importKey(keyName, pem, 'mypassword', (err, key) => { - expect(err).to.not.exist() - expect(key).to.exist() - expect(key).to.have.property('name', keyName) - expect(key).to.have.property('id', 'QmeMWBbuyw8KycYhZVxMzVHK3zLH1mp2DT84X2NApqiXgn') - ks.removeKey(keyName, done) - }) - }) - }) -}