Giter VIP home page Giter VIP logo

forge's Introduction

Forge

npm package

Build Status

A native implementation of TLS (and various other cryptographic tools) in JavaScript.

Introduction

The Forge software is a fully native implementation of the TLS protocol in JavaScript, a set of cryptography utilities, and a set of tools for developing Web Apps that utilize many network resources.

Performance

Forge is fast. Benchmarks against other popular JavaScript cryptography libraries can be found here:

Documentation

API

Transports

Ciphers

PKI

Message Digests

Utilities

Other


Installation

Note: Please see the Security Considerations section before using packaging systems and pre-built files.

Forge uses a CommonJS module structure with a build process for browser bundles. The older 0.6.x branch with standalone files is available but will not be regularly updated.

Node.js

If you want to use forge with Node.js, it is available through npm:

https://www.npmjs.com/package/node-forge

Installation:

npm install node-forge

You can then use forge as a regular module:

var forge = require('node-forge');

The npm package includes pre-built forge.min.js, forge.all.min.js, and prime.worker.min.js using the UMD format.

jsDelivr CDN

To use it via jsDelivr include this in your html:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/forge.min.js"></script>

unpkg CDN

To use it via unpkg include this in your html:

<script src="https://unpkg.com/[email protected]/dist/forge.min.js"></script>

Development Requirements

The core JavaScript has the following requirements to build and test:

  • Building a browser bundle:
    • Node.js
    • npm
  • Testing
    • Node.js
    • npm
    • Chrome, Firefox, Safari (optional)

Some special networking features can optionally use a Flash component. See the Flash README for details.

Building for a web browser

To create single file bundles for use with browsers run the following:

npm install
npm run build

This will create single non-minimized and minimized files that can be included in the browser:

dist/forge.js
dist/forge.min.js

A bundle that adds some utilities and networking support is also available:

dist/forge.all.js
dist/forge.all.min.js

Include the file via:

<script src="YOUR_SCRIPT_PATH/forge.js"></script>

or

<script src="YOUR_SCRIPT_PATH/forge.min.js"></script>

The above bundles will synchronously create a global 'forge' object.

Note: These bundles will not include any WebWorker scripts (eg: dist/prime.worker.js), so these will need to be accessible from the browser if any WebWorkers are used.

Building a custom browser bundle

The build process uses webpack and the config file can be modified to generate a file or files that only contain the parts of forge you need.

Browserify override support is also present in package.json.

Testing

Prepare to run tests

npm install

Running automated tests with Node.js

Forge natively runs in a Node.js environment:

npm test

Running automated tests with Headless Chrome

Automated testing is done via Karma. By default it will run the tests with Headless Chrome.

npm run test-karma

Is 'mocha' reporter output too verbose? Other reporters are available. Try 'dots', 'progress', or 'tap'.

npm run test-karma -- --reporters progress

By default webpack is used. Browserify can also be used.

BUNDLER=browserify npm run test-karma

Running automated tests with one or more browsers

You can also specify one or more browsers to use.

npm run test-karma -- --browsers Chrome,Firefox,Safari,ChromeHeadless

The reporter option and BUNDLER environment variable can also be used.

Running manual tests in a browser

Testing in a browser uses webpack to combine forge and all tests and then loading the result in a browser. A simple web server is provided that will output the HTTP or HTTPS URLs to load. It also will start a simple Flash Policy Server. Unit tests and older legacy tests are provided. Custom ports can be used by running node tests/server.js manually.

To run the unit tests in a browser a special forge build is required:

npm run test-build

To run legacy browser based tests the main forge build is required:

npm run build

The tests are run with a custom server that prints out the URLs to use:

npm run test-server

Running other tests

There are some other random tests and benchmarks available in the tests directory.

Coverage testing

To perform coverage testing of the unit tests, run the following. The results will be put in the coverage/ directory. Note that coverage testing can slow down some tests considerably.

npm install
npm run coverage

Contributing

Any contributions (eg: PRs) that are accepted will be brought under the same license used by the rest of the Forge project. This license allows Forge to be used under the terms of either the BSD License or the GNU General Public License (GPL) Version 2.

See: LICENSE

If a contribution contains 3rd party source code with its own license, it may retain it, so long as that license is compatible with the Forge license.

API

If at any time you wish to disable the use of native code, where available, for particular forge features like its secure random number generator, you may set the forge.options.usePureJavaScript flag to true. It is not recommended that you set this flag as native code is typically more performant and may have stronger security properties. It may be useful to set this flag to test certain features that you plan to run in environments that are different from your testing environment.

To disable native code when including forge in the browser:

// run this *after* including the forge script
forge.options.usePureJavaScript = true;

To disable native code when using Node.js:

var forge = require('node-forge');
forge.options.usePureJavaScript = true;

Transports

Provides a native javascript client and server-side TLS implementation.

Examples

// create TLS client
var client = forge.tls.createConnection({
  server: false,
  caStore: /* Array of PEM-formatted certs or a CA store object */,
  sessionCache: {},
  // supported cipher suites in order of preference
  cipherSuites: [
    forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
    forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
  virtualHost: 'example.com',
  verify: function(connection, verified, depth, certs) {
    if(depth === 0) {
      var cn = certs[0].subject.getField('CN').value;
      if(cn !== 'example.com') {
        verified = {
          alert: forge.tls.Alert.Description.bad_certificate,
          message: 'Certificate common name does not match hostname.'
        };
      }
    }
    return verified;
  },
  connected: function(connection) {
    console.log('connected');
    // send message to server
    connection.prepare(forge.util.encodeUtf8('Hi server!'));
    /* NOTE: experimental, start heartbeat retransmission timer
    myHeartbeatTimer = setInterval(function() {
      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
    }, 5*60*1000);*/
  },
  /* provide a client-side cert if you want
  getCertificate: function(connection, hint) {
    return myClientCertificate;
  },
  /* the private key for the client-side cert if provided */
  getPrivateKey: function(connection, cert) {
    return myClientPrivateKey;
  },
  tlsDataReady: function(connection) {
    // TLS data (encrypted) is ready to be sent to the server
    sendToServerSomehow(connection.tlsData.getBytes());
    // if you were communicating with the server below, you'd do:
    // server.process(connection.tlsData.getBytes());
  },
  dataReady: function(connection) {
    // clear data from the server is ready
    console.log('the server sent: ' +
      forge.util.decodeUtf8(connection.data.getBytes()));
    // close connection
    connection.close();
  },
  /* NOTE: experimental
  heartbeatReceived: function(connection, payload) {
    // restart retransmission timer, look at payload
    clearInterval(myHeartbeatTimer);
    myHeartbeatTimer = setInterval(function() {
      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
    }, 5*60*1000);
    payload.getBytes();
  },*/
  closed: function(connection) {
    console.log('disconnected');
  },
  error: function(connection, error) {
    console.log('uh oh', error);
  }
});

// start the handshake process
client.handshake();

// when encrypted TLS data is received from the server, process it
client.process(encryptedBytesFromServer);

// create TLS server
var server = forge.tls.createConnection({
  server: true,
  caStore: /* Array of PEM-formatted certs or a CA store object */,
  sessionCache: {},
  // supported cipher suites in order of preference
  cipherSuites: [
    forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
    forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
  // require a client-side certificate if you want
  verifyClient: true,
  verify: function(connection, verified, depth, certs) {
    if(depth === 0) {
      var cn = certs[0].subject.getField('CN').value;
      if(cn !== 'the-client') {
        verified = {
          alert: forge.tls.Alert.Description.bad_certificate,
          message: 'Certificate common name does not match expected client.'
        };
      }
    }
    return verified;
  },
  connected: function(connection) {
    console.log('connected');
    // send message to client
    connection.prepare(forge.util.encodeUtf8('Hi client!'));
    /* NOTE: experimental, start heartbeat retransmission timer
    myHeartbeatTimer = setInterval(function() {
      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
    }, 5*60*1000);*/
  },
  getCertificate: function(connection, hint) {
    return myServerCertificate;
  },
  getPrivateKey: function(connection, cert) {
    return myServerPrivateKey;
  },
  tlsDataReady: function(connection) {
    // TLS data (encrypted) is ready to be sent to the client
    sendToClientSomehow(connection.tlsData.getBytes());
    // if you were communicating with the client above you'd do:
    // client.process(connection.tlsData.getBytes());
  },
  dataReady: function(connection) {
    // clear data from the client is ready
    console.log('the client sent: ' +
      forge.util.decodeUtf8(connection.data.getBytes()));
    // close connection
    connection.close();
  },
  /* NOTE: experimental
  heartbeatReceived: function(connection, payload) {
    // restart retransmission timer, look at payload
    clearInterval(myHeartbeatTimer);
    myHeartbeatTimer = setInterval(function() {
      connection.prepareHeartbeatRequest(forge.util.createBuffer('1234'));
    }, 5*60*1000);
    payload.getBytes();
  },*/
  closed: function(connection) {
    console.log('disconnected');
  },
  error: function(connection, error) {
    console.log('uh oh', error);
  }
});

// when encrypted TLS data is received from the client, process it
server.process(encryptedBytesFromClient);

Connect to a TLS server using node's net.Socket:

var socket = new net.Socket();

var client = forge.tls.createConnection({
  server: false,
  verify: function(connection, verified, depth, certs) {
    // skip verification for testing
    console.log('[tls] server certificate verified');
    return true;
  },
  connected: function(connection) {
    console.log('[tls] connected');
    // prepare some data to send (note that the string is interpreted as
    // 'binary' encoded, which works for HTTP which only uses ASCII, use
    // forge.util.encodeUtf8(str) otherwise
    client.prepare('GET / HTTP/1.0\r\n\r\n');
  },
  tlsDataReady: function(connection) {
    // encrypted data is ready to be sent to the server
    var data = connection.tlsData.getBytes();
    socket.write(data, 'binary'); // encoding should be 'binary'
  },
  dataReady: function(connection) {
    // clear data from the server is ready
    var data = connection.data.getBytes();
    console.log('[tls] data received from the server: ' + data);
  },
  closed: function() {
    console.log('[tls] disconnected');
  },
  error: function(connection, error) {
    console.log('[tls] error', error);
  }
});

socket.on('connect', function() {
  console.log('[socket] connected');
  client.handshake();
});
socket.on('data', function(data) {
  client.process(data.toString('binary')); // encoding should be 'binary'
});
socket.on('end', function() {
  console.log('[socket] disconnected');
});

// connect to google.com
socket.connect(443, 'google.com');

// or connect to gmail's imap server (but don't send the HTTP header above)
//socket.connect(993, 'imap.gmail.com');

Provides a native JavaScript mini-implementation of an http client that uses pooled sockets.

Examples

// create an HTTP GET request
var request = forge.http.createRequest({method: 'GET', path: url.path});

// send the request somewhere
sendSomehow(request.toString());

// receive response
var buffer = forge.util.createBuffer();
var response = forge.http.createResponse();
var someAsyncDataHandler = function(bytes) {
  if(!response.bodyReceived) {
    buffer.putBytes(bytes);
    if(!response.headerReceived) {
      if(response.readHeader(buffer)) {
        console.log('HTTP response header: ' + response.toString());
      }
    }
    if(response.headerReceived && !response.bodyReceived) {
      if(response.readBody(buffer)) {
        console.log('HTTP response body: ' + response.body);
      }
    }
  }
};

Provides some SSH utility functions.

Examples

// encodes (and optionally encrypts) a private RSA key as a Putty PPK file
forge.ssh.privateKeyToPutty(privateKey, passphrase, comment);

// encodes a public RSA key as an OpenSSH file
forge.ssh.publicKeyToOpenSSH(key, comment);

// encodes a private RSA key as an OpenSSH file
forge.ssh.privateKeyToOpenSSH(privateKey, passphrase);

// gets the SSH public key fingerprint in a byte buffer
forge.ssh.getPublicKeyFingerprint(key);

// gets a hex-encoded, colon-delimited SSH public key fingerprint
forge.ssh.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});

Provides an XmlHttpRequest implementation using forge.http as a backend.

Examples

// TODO

Provides an interface to create and use raw sockets provided via Flash.

Examples

// TODO

Ciphers

Provides a basic API for block encryption and decryption. There is built-in support for the ciphers: AES, 3DES, and DES, and for the modes of operation: ECB, CBC, CFB, OFB, CTR, and GCM.

These algorithms are currently supported:

  • AES-ECB
  • AES-CBC
  • AES-CFB
  • AES-OFB
  • AES-CTR
  • AES-GCM
  • 3DES-ECB
  • 3DES-CBC
  • DES-ECB
  • DES-CBC

When using an AES algorithm, the key size will determine whether AES-128, AES-192, or AES-256 is used (all are supported). When a DES algorithm is used, the key size will determine whether 3DES or regular DES is used. Use a 3DES algorithm to enforce Triple-DES.

Examples

// generate a random key and IV
// Note: a key size of 16 bytes will use AES-128, 24 => AES-192, 32 => AES-256
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(16);

/* alternatively, generate a password-based 16-byte key
var salt = forge.random.getBytesSync(128);
var key = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
*/

// encrypt some bytes using CBC mode
// (other modes include: ECB, CFB, OFB, CTR, and GCM)
// Note: CBC and ECB modes use PKCS#7 padding as default
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());

// decrypt some bytes using CBC mode
// (other modes include: CFB, OFB, CTR, and GCM)
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(encrypted);
var result = decipher.finish(); // check 'result' for true/false
// outputs decrypted hex
console.log(decipher.output.toHex());

// decrypt bytes using CBC mode and streaming
// Performance can suffer for large multi-MB inputs due to buffer
// manipulations. Stream processing in chunks can offer significant
// improvement. CPU intensive update() calls could also be performed with
// setImmediate/setTimeout to avoid blocking the main browser UI thread (not
// shown here). Optimal block size depends on the JavaScript VM and other
// factors. Encryption can use a simple technique for increased performance.
var encryptedBytes = encrypted.bytes();
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
var length = encryptedBytes.length;
var chunkSize = 1024 * 64;
var index = 0;
var decrypted = '';
do {
  decrypted += decipher.output.getBytes();
  var buf = forge.util.createBuffer(encryptedBytes.substr(index, chunkSize));
  decipher.update(buf);
  index += chunkSize;
} while(index < length);
var result = decipher.finish();
assert(result);
decrypted += decipher.output.getBytes();
console.log(forge.util.bytesToHex(decrypted));

// encrypt some bytes using GCM mode
var cipher = forge.cipher.createCipher('AES-GCM', key);
cipher.start({
  iv: iv, // should be a 12-byte binary-encoded string or byte buffer
  additionalData: 'binary-encoded string', // optional
  tagLength: 128 // optional, defaults to 128 bits
});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
var tag = cipher.mode.tag;
// outputs encrypted hex
console.log(encrypted.toHex());
// outputs authentication tag
console.log(tag.toHex());

// decrypt some bytes using GCM mode
var decipher = forge.cipher.createDecipher('AES-GCM', key);
decipher.start({
  iv: iv,
  additionalData: 'binary-encoded string', // optional
  tagLength: 128, // optional, defaults to 128 bits
  tag: tag // authentication tag from encryption
});
decipher.update(encrypted);
var pass = decipher.finish();
// pass is false if there was a failure (eg: authentication tag didn't match)
if(pass) {
  // outputs decrypted hex
  console.log(decipher.output.toHex());
}

Using forge in Node.js to match openssl's "enc" command line tool (Note: OpenSSL "enc" uses a non-standard file format with a custom key derivation function and a fixed iteration count of 1, which some consider less secure than alternatives such as OpenPGP/GnuPG):

var forge = require('node-forge');
var fs = require('fs');

// openssl enc -des3 -in input.txt -out input.enc
function encrypt(password) {
  var input = fs.readFileSync('input.txt', {encoding: 'binary'});

  // 3DES key and IV sizes
  var keySize = 24;
  var ivSize = 8;

  // get derived bytes
  // Notes:
  // 1. If using an alternative hash (eg: "-md sha1") pass
  //   "forge.md.sha1.create()" as the final parameter.
  // 2. If using "-nosalt", set salt to null.
  var salt = forge.random.getBytesSync(8);
  // var md = forge.md.sha1.create(); // "-md sha1"
  var derivedBytes = forge.pbe.opensslDeriveBytes(
    password, salt, keySize + ivSize/*, md*/);
  var buffer = forge.util.createBuffer(derivedBytes);
  var key = buffer.getBytes(keySize);
  var iv = buffer.getBytes(ivSize);

  var cipher = forge.cipher.createCipher('3DES-CBC', key);
  cipher.start({iv: iv});
  cipher.update(forge.util.createBuffer(input, 'binary'));
  cipher.finish();

  var output = forge.util.createBuffer();

  // if using a salt, prepend this to the output:
  if(salt !== null) {
    output.putBytes('Salted__'); // (add to match openssl tool output)
    output.putBytes(salt);
  }
  output.putBuffer(cipher.output);

  fs.writeFileSync('input.enc', output.getBytes(), {encoding: 'binary'});
}

// openssl enc -d -des3 -in input.enc -out input.dec.txt
function decrypt(password) {
  var input = fs.readFileSync('input.enc', {encoding: 'binary'});

  // parse salt from input
  input = forge.util.createBuffer(input, 'binary');
  // skip "Salted__" (if known to be present)
  input.getBytes('Salted__'.length);
  // read 8-byte salt
  var salt = input.getBytes(8);

  // Note: if using "-nosalt", skip above parsing and use
  // var salt = null;

  // 3DES key and IV sizes
  var keySize = 24;
  var ivSize = 8;

  var derivedBytes = forge.pbe.opensslDeriveBytes(
    password, salt, keySize + ivSize);
  var buffer = forge.util.createBuffer(derivedBytes);
  var key = buffer.getBytes(keySize);
  var iv = buffer.getBytes(ivSize);

  var decipher = forge.cipher.createDecipher('3DES-CBC', key);
  decipher.start({iv: iv});
  decipher.update(input);
  var result = decipher.finish(); // check 'result' for true/false

  fs.writeFileSync(
    'input.dec.txt', decipher.output.getBytes(), {encoding: 'binary'});
}

Provides AES encryption and decryption in CBC, CFB, OFB, CTR, and GCM modes. See CIPHER for examples.

Provides 3DES and DES encryption and decryption in ECB and CBC modes. See CIPHER for examples.

Examples

// generate a random key and IV
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(8);

// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());

// decrypt some bytes
var cipher = forge.rc2.createDecryptionCipher(key);
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
// outputs decrypted hex
console.log(cipher.output.toHex());

PKI

Provides X.509 certificate support, ED25519 key generation and signing/verifying, and RSA public and private key encoding, decoding, encryption/decryption, and signing/verifying.

Special thanks to TweetNaCl.js for providing the bulk of the implementation.

Examples

var ed25519 = forge.pki.ed25519;

// generate a random ED25519 keypair
var keypair = ed25519.generateKeyPair();
// `keypair.publicKey` is a node.js Buffer or Uint8Array
// `keypair.privateKey` is a node.js Buffer or Uint8Array

// generate a random ED25519 keypair based on a random 32-byte seed
var seed = forge.random.getBytesSync(32);
var keypair = ed25519.generateKeyPair({seed: seed});

// generate a random ED25519 keypair based on a "password" 32-byte seed
var password = 'Mai9ohgh6ahxee0jutheew0pungoozil';
var seed = new forge.util.ByteBuffer(password, 'utf8');
var keypair = ed25519.generateKeyPair({seed: seed});

// sign a UTF-8 message
var signature = ED25519.sign({
  message: 'test',
  // also accepts `binary` if you want to pass a binary string
  encoding: 'utf8',
  // node.js Buffer, Uint8Array, forge ByteBuffer, binary string
  privateKey: privateKey
});
// `signature` is a node.js Buffer or Uint8Array

// sign a message passed as a buffer
var signature = ED25519.sign({
  // also accepts a forge ByteBuffer or Uint8Array
  message: Buffer.from('test', 'utf8'),
  privateKey: privateKey
});

// sign a message digest (shorter "message" == better performance)
var md = forge.md.sha256.create();
md.update('test', 'utf8');
var signature = ED25519.sign({
  md: md,
  privateKey: privateKey
});

// verify a signature on a UTF-8 message
var verified = ED25519.verify({
  message: 'test',
  encoding: 'utf8',
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  signature: signature,
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  publicKey: publicKey
});
// `verified` is true/false

// sign a message passed as a buffer
var verified = ED25519.verify({
  // also accepts a forge ByteBuffer or Uint8Array
  message: Buffer.from('test', 'utf8'),
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  signature: signature,
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  publicKey: publicKey
});

// verify a signature on a message digest
var md = forge.md.sha256.create();
md.update('test', 'utf8');
var verified = ED25519.verify({
  md: md,
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  signature: signature,
  // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string
  publicKey: publicKey
});

Examples

var rsa = forge.pki.rsa;

// generate an RSA key pair synchronously
// *NOT RECOMMENDED*: Can be significantly slower than async and may block
// JavaScript execution. Will use native Node.js 10.12.0+ API if possible.
var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001});

// generate an RSA key pair asynchronously (uses web workers if available)
// use workers: -1 to run a fast core estimator to optimize # of workers
// *RECOMMENDED*: Can be significantly faster than sync. Will use native
// Node.js 10.12.0+ or WebCrypto API if possible.
rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) {
  // keypair.privateKey, keypair.publicKey
});

// generate an RSA key pair in steps that attempt to run for a specified period
// of time on the main JS thread
var state = rsa.createKeyPairGenerationState(2048, 0x10001);
var step = function() {
  // run for 100 ms
  if(!rsa.stepKeyPairGenerationState(state, 100)) {
    setTimeout(step, 1);
  }
  else {
    // done, turn off progress indicator, use state.keys
  }
};
// turn on progress indicator, schedule generation to run
setTimeout(step);

// sign data with a private key and output DigestInfo DER-encoded bytes
// (defaults to RSASSA PKCS#1 v1.5)
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
var signature = privateKey.sign(md);

// verify data with a public key
// (defaults to RSASSA PKCS#1 v1.5)
var verified = publicKey.verify(md.digest().bytes(), signature);

// sign data using RSASSA-PSS where PSS uses a SHA-1 hash, a SHA-1 based
// masking function MGF1, and a 20 byte salt
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
var pss = forge.pss.create({
  md: forge.md.sha1.create(),
  mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
  saltLength: 20
  // optionally pass 'prng' with a custom PRNG implementation
  // optionalls pass 'salt' with a forge.util.ByteBuffer w/custom salt
});
var signature = privateKey.sign(md, pss);

// verify RSASSA-PSS signature
var pss = forge.pss.create({
  md: forge.md.sha1.create(),
  mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
  saltLength: 20
  // optionally pass 'prng' with a custom PRNG implementation
});
var md = forge.md.sha1.create();
md.update('sign this', 'utf8');
publicKey.verify(md.digest().getBytes(), signature, pss);

// encrypt data with a public key (defaults to RSAES PKCS#1 v1.5)
var encrypted = publicKey.encrypt(bytes);

// decrypt data with a private key (defaults to RSAES PKCS#1 v1.5)
var decrypted = privateKey.decrypt(encrypted);

// encrypt data with a public key using RSAES PKCS#1 v1.5
var encrypted = publicKey.encrypt(bytes, 'RSAES-PKCS1-V1_5');

// decrypt data with a private key using RSAES PKCS#1 v1.5
var decrypted = privateKey.decrypt(encrypted, 'RSAES-PKCS1-V1_5');

// encrypt data with a public key using RSAES-OAEP
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP');

// decrypt data with a private key using RSAES-OAEP
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP');

// encrypt data with a public key using RSAES-OAEP/SHA-256
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
  md: forge.md.sha256.create()
});

// decrypt data with a private key using RSAES-OAEP/SHA-256
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
  md: forge.md.sha256.create()
});

// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  mgf1: {
    md: forge.md.sha1.create()
  }
});

// decrypt data with a private key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var decrypted = privateKey.decrypt(encrypted, 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  mgf1: {
    md: forge.md.sha1.create()
  }
});

Examples

// generate an RSA key pair asynchronously (uses web workers if available)
// use workers: -1 to run a fast core estimator to optimize # of workers
forge.rsa.generateKeyPair({bits: 2048, workers: -1}, function(err, keypair) {
  // keypair.privateKey, keypair.publicKey
});

// generate and encapsulate a 16-byte secret key
var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
var kem = forge.kem.rsa.create(kdf1);
var result = kem.encrypt(keypair.publicKey, 16);
// result has 'encapsulation' and 'key'

// encrypt some bytes
var iv = forge.random.getBytesSync(12);
var someBytes = 'hello world!';
var cipher = forge.cipher.createCipher('AES-GCM', result.key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output.getBytes();
var tag = cipher.mode.tag.getBytes();

// send 'encrypted', 'iv', 'tag', and result.encapsulation to recipient

// decrypt encapsulated 16-byte secret key
var kdf1 = new forge.kem.kdf1(forge.md.sha1.create());
var kem = forge.kem.rsa.create(kdf1);
var key = kem.decrypt(keypair.privateKey, result.encapsulation, 16);

// decrypt some bytes
var decipher = forge.cipher.createDecipher('AES-GCM', key);
decipher.start({iv: iv, tag: tag});
decipher.update(forge.util.createBuffer(encrypted));
var pass = decipher.finish();
// pass is false if there was a failure (eg: authentication tag didn't match)
if(pass) {
  // outputs 'hello world!'
  console.log(decipher.output.getBytes());
}

Examples

var pki = forge.pki;

// convert a PEM-formatted public key to a Forge public key
var publicKey = pki.publicKeyFromPem(pem);

// convert a Forge public key to PEM-format
var pem = pki.publicKeyToPem(publicKey);

// convert an ASN.1 SubjectPublicKeyInfo to a Forge public key
var publicKey = pki.publicKeyFromAsn1(subjectPublicKeyInfo);

// convert a Forge public key to an ASN.1 SubjectPublicKeyInfo
var subjectPublicKeyInfo = pki.publicKeyToAsn1(publicKey);

// gets a SHA-1 RSAPublicKey fingerprint a byte buffer
pki.getPublicKeyFingerprint(key);

// gets a SHA-1 SubjectPublicKeyInfo fingerprint a byte buffer
pki.getPublicKeyFingerprint(key, {type: 'SubjectPublicKeyInfo'});

// gets a hex-encoded, colon-delimited SHA-1 RSAPublicKey public key fingerprint
pki.getPublicKeyFingerprint(key, {encoding: 'hex', delimiter: ':'});

// gets a hex-encoded, colon-delimited SHA-1 SubjectPublicKeyInfo public key fingerprint
pki.getPublicKeyFingerprint(key, {
  type: 'SubjectPublicKeyInfo',
  encoding: 'hex',
  delimiter: ':'
});

// gets a hex-encoded, colon-delimited MD5 RSAPublicKey public key fingerprint
pki.getPublicKeyFingerprint(key, {
  md: forge.md.md5.create(),
  encoding: 'hex',
  delimiter: ':'
});

// creates a CA store
var caStore = pki.createCaStore([/* PEM-encoded cert */, ...]);

// add a certificate to the CA store
caStore.addCertificate(certObjectOrPemString);

// gets the issuer (its certificate) for the given certificate
var issuerCert = caStore.getIssuer(subjectCert);

// verifies a certificate chain against a CA store
pki.verifyCertificateChain(caStore, chain, customVerifyCallback);

// signs a certificate using the given private key
cert.sign(privateKey);

// signs a certificate using SHA-256 instead of SHA-1
cert.sign(privateKey, forge.md.sha256.create());

// verifies an issued certificate using the certificates public key
var verified = issuer.verify(issued);

// generate a keypair and create an X.509v3 certificate
var keys = pki.rsa.generateKeyPair(2048);
var cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
// alternatively set public key from a csr
//cert.publicKey = csr.publicKey;
// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
// Conforming CAs should ensure serialNumber is:
// - no more than 20 octets
// - non-negative (prefix a '00' if your value starts with a '1' bit)
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
var attrs = [{
  name: 'commonName',
  value: 'example.org'
}, {
  name: 'countryName',
  value: 'US'
}, {
  shortName: 'ST',
  value: 'Virginia'
}, {
  name: 'localityName',
  value: 'Blacksburg'
}, {
  name: 'organizationName',
  value: 'Test'
}, {
  shortName: 'OU',
  value: 'Test'
}];
cert.setSubject(attrs);
// alternatively set subject from a csr
//cert.setSubject(csr.subject.attributes);
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
}, {
  name: 'subjectAltName',
  altNames: [{
    type: 6, // URI
    value: 'http://example.org/webid#me'
  }, {
    type: 7, // IP
    ip: '127.0.0.1'
  }]
}, {
  name: 'subjectKeyIdentifier'
}]);
/* alternatively set extensions from a csr
var extensions = csr.getAttribute({name: 'extensionRequest'}).extensions;
// optionally add more extensions
extensions.push.apply(extensions, [{
  name: 'basicConstraints',
  cA: true
}, {
  name: 'keyUsage',
  keyCertSign: true,
  digitalSignature: true,
  nonRepudiation: true,
  keyEncipherment: true,
  dataEncipherment: true
}]);
cert.setExtensions(extensions);
*/
// self-sign certificate
cert.sign(keys.privateKey);

// convert a Forge certificate to PEM
var pem = pki.certificateToPem(cert);

// convert a Forge certificate from PEM
var cert = pki.certificateFromPem(pem);

// convert an ASN.1 X.509x3 object to a Forge certificate
var cert = pki.certificateFromAsn1(obj);

// convert a Forge certificate to an ASN.1 X.509v3 object
var asn1Cert = pki.certificateToAsn1(cert);

Provides the password-based key-derivation function from PKCS#5.

Examples

// generate a password-based 16-byte key
// note an optional message digest can be passed as the final parameter
var salt = forge.random.getBytesSync(128);
var derivedKey = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);

// generate key asynchronously
// note an optional message digest can be passed before the callback
forge.pkcs5.pbkdf2('password', salt, numIterations, 16, function(err, derivedKey) {
  // do something w/derivedKey
});

Provides cryptographically protected messages from PKCS#7.

Examples

// convert a message from PEM
var p7 = forge.pkcs7.messageFromPem(pem);
// look at p7.recipients

// find a recipient by the issuer of a certificate
var recipient = p7.findRecipient(cert);

// decrypt
p7.decrypt(p7.recipients[0], privateKey);

// create a p7 enveloped message
var p7 = forge.pkcs7.createEnvelopedData();

// add a recipient
var cert = forge.pki.certificateFromPem(certPem);
p7.addRecipient(cert);

// set content
p7.content = forge.util.createBuffer('Hello');

// encrypt
p7.encrypt();

// convert message to PEM
var pem = forge.pkcs7.messageToPem(p7);

// create a degenerate PKCS#7 certificate container
// (CRLs not currently supported, only certificates)
var p7 = forge.pkcs7.createSignedData();
p7.addCertificate(certOrCertPem1);
p7.addCertificate(certOrCertPem2);
var pem = forge.pkcs7.messageToPem(p7);

// create PKCS#7 signed data with authenticatedAttributes
// attributes include: PKCS#9 content-type, message-digest, and signing-time
var p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer('Some content to be signed.', 'utf8');
p7.addCertificate(certOrCertPem);
p7.addSigner({
  key: privateKeyAssociatedWithCert,
  certificate: certOrCertPem,
  digestAlgorithm: forge.pki.oids.sha256,
  authenticatedAttributes: [{
    type: forge.pki.oids.contentType,
    value: forge.pki.oids.data
  }, {
    type: forge.pki.oids.messageDigest
    // value will be auto-populated at signing time
  }, {
    type: forge.pki.oids.signingTime,
    // value can also be auto-populated at signing time
    value: new Date()
  }]
});
p7.sign();
var pem = forge.pkcs7.messageToPem(p7);

// PKCS#7 Sign in detached mode.
// Includes the signature and certificate without the signed data.
p7.sign({detached: true});

Examples

var pki = forge.pki;

// convert a PEM-formatted private key to a Forge private key
var privateKey = pki.privateKeyFromPem(pem);

// convert a Forge private key to PEM-format
var pem = pki.privateKeyToPem(privateKey);

// convert an ASN.1 PrivateKeyInfo or RSAPrivateKey to a Forge private key
var privateKey = pki.privateKeyFromAsn1(rsaPrivateKey);

// convert a Forge private key to an ASN.1 RSAPrivateKey
var rsaPrivateKey = pki.privateKeyToAsn1(privateKey);

// wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
var privateKeyInfo = pki.wrapRsaPrivateKey(rsaPrivateKey);

// convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
var pem = pki.privateKeyInfoToPem(privateKeyInfo);

// encrypts a PrivateKeyInfo using a custom password and
// outputs an EncryptedPrivateKeyInfo
var encryptedPrivateKeyInfo = pki.encryptPrivateKeyInfo(
  privateKeyInfo, 'myCustomPasswordHere', {
    algorithm: 'aes256', // 'aes128', 'aes192', 'aes256', '3des'
  });

// decrypts an ASN.1 EncryptedPrivateKeyInfo that was encrypted
// with a custom password
var privateKeyInfo = pki.decryptPrivateKeyInfo(
  encryptedPrivateKeyInfo, 'myCustomPasswordHere');

// converts an EncryptedPrivateKeyInfo to PEM
var pem = pki.encryptedPrivateKeyToPem(encryptedPrivateKeyInfo);

// converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format
var encryptedPrivateKeyInfo = pki.encryptedPrivateKeyFromPem(pem);

// wraps and encrypts a Forge private key and outputs it in PEM format
var pem = pki.encryptRsaPrivateKey(privateKey, 'password');

// encrypts a Forge private key and outputs it in PEM format using OpenSSL's
// proprietary legacy format + encapsulated PEM headers (DEK-Info)
var pem = pki.encryptRsaPrivateKey(privateKey, 'password', {legacy: true});

// decrypts a PEM-formatted, encrypted private key
var privateKey = pki.decryptRsaPrivateKey(pem, 'password');

// sets an RSA public key from a private key
var publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e);

Provides certification requests or certificate signing requests (CSR) from PKCS#10.

Examples

// generate a key pair
var keys = forge.pki.rsa.generateKeyPair(2048);

// create a certification request (CSR)
var csr = forge.pki.createCertificationRequest();
csr.publicKey = keys.publicKey;
csr.setSubject([{
  name: 'commonName',
  value: 'example.org'
}, {
  name: 'countryName',
  value: 'US'
}, {
  shortName: 'ST',
  value: 'Virginia'
}, {
  name: 'localityName',
  value: 'Blacksburg'
}, {
  name: 'organizationName',
  value: 'Test'
}, {
  shortName: 'OU',
  value: 'Test'
}]);
// set (optional) attributes
csr.setAttributes([{
  name: 'challengePassword',
  value: 'password'
}, {
  name: 'unstructuredName',
  value: 'My Company, Inc.'
}, {
  name: 'extensionRequest',
  extensions: [{
    name: 'subjectAltName',
    altNames: [{
      // 2 is DNS type
      type: 2,
      value: 'test.domain.com'
    }, {
      type: 2,
      value: 'other.domain.com',
    }, {
      type: 2,
      value: 'www.domain.net'
    }]
  }]
}]);

// sign certification request
csr.sign(keys.privateKey);

// verify certification request
var verified = csr.verify();

// convert certification request to PEM-format
var pem = forge.pki.certificationRequestToPem(csr);

// convert a Forge certification request from PEM-format
var csr = forge.pki.certificationRequestFromPem(pem);

// get an attribute
csr.getAttribute({name: 'challengePassword'});

// get extensions array
csr.getAttribute({name: 'extensionRequest'}).extensions;

Provides the cryptographic archive file format from PKCS#12.

Note for Chrome/Firefox/iOS/similar users: If you have trouble importing a PKCS#12 container, try using the TripleDES algorithm. It can be passed to forge.pkcs12.toPkcs12Asn1 using the {algorithm: '3des'} option.

Examples

// decode p12 from base64
var p12Der = forge.util.decode64(p12b64);
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(p12Der);
// decrypt p12 using the password 'password'
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
// decrypt p12 using non-strict parsing mode (resolves some ASN.1 parse errors)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, 'password');
// decrypt p12 using literally no password (eg: Mac OS X/apple push)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1);
// decrypt p12 using an "empty" password (eg: OpenSSL with no password input)
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, '');
// p12.safeContents is an array of safe contents, each of
// which contains an array of safeBags

// get bags by friendlyName
var bags = p12.getBags({friendlyName: 'test'});
// bags are key'd by attribute type (here "friendlyName")
// and the key values are an array of matching objects
var cert = bags.friendlyName[0];

// get bags by localKeyId
var bags = p12.getBags({localKeyId: buffer});
// bags are key'd by attribute type (here "localKeyId")
// and the key values are an array of matching objects
var cert = bags.localKeyId[0];

// get bags by localKeyId (input in hex)
var bags = p12.getBags({localKeyIdHex: '7b59377ff142d0be4565e9ac3d396c01401cd879'});
// bags are key'd by attribute type (here "localKeyId", *not* "localKeyIdHex")
// and the key values are an array of matching objects
var cert = bags.localKeyId[0];

// get bags by type
var bags = p12.getBags({bagType: forge.pki.oids.certBag});
// bags are key'd by bagType and each bagType key's value
// is an array of matches (in this case, certificate objects)
var cert = bags[forge.pki.oids.certBag][0];

// get bags by friendlyName and filter on bag type
var bags = p12.getBags({
  friendlyName: 'test',
  bagType: forge.pki.oids.certBag
});

// get key bags
var bags = p12.getBags({bagType: forge.pki.oids.keyBag});
// get key
var bag = bags[forge.pki.oids.keyBag][0];
var key = bag.key;
// if the key is in a format unrecognized by forge then
// bag.key will be `null`, use bag.asn1 to get the ASN.1
// representation of the key
if(bag.key === null) {
  var keyAsn1 = bag.asn1;
  // can now convert back to DER/PEM/etc for export
}

// generate a p12 using AES (default)
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
  privateKey, certificateChain, 'password');

// generate a p12 that can be imported by Chrome/Firefox/iOS
// (requires the use of Triple DES instead of AES)
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
  privateKey, certificateChain, 'password',
  {algorithm: '3des'});

// base64-encode p12
var p12Der = forge.asn1.toDer(p12Asn1).getBytes();
var p12b64 = forge.util.encode64(p12Der);

// create download link for p12
var a = document.createElement('a');
a.download = 'example.p12';
a.setAttribute('href', 'data:application/x-pkcs12;base64,' + p12b64);
a.appendChild(document.createTextNode('Download'));

Provides ASN.1 DER encoding and decoding.

Examples

var asn1 = forge.asn1;

// create a SubjectPublicKeyInfo
var subjectPublicKeyInfo =
  asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
    // AlgorithmIdentifier
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
      // algorithm
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
        asn1.oidToDer(pki.oids['rsaEncryption']).getBytes()),
      // parameters (null)
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
    ]),
    // subjectPublicKey
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [
      // RSAPublicKey
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
        // modulus (n)
        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
          _bnToBytes(key.n)),
        // publicExponent (e)
        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
          _bnToBytes(key.e))
      ])
    ])
  ]);

// serialize an ASN.1 object to DER format
var derBuffer = asn1.toDer(subjectPublicKeyInfo);

// deserialize to an ASN.1 object from a byte buffer filled with DER data
var object = asn1.fromDer(derBuffer);

// convert an OID dot-separated string to a byte buffer
var derOidBuffer = asn1.oidToDer('1.2.840.113549.1.1.5');

// convert a byte buffer with a DER-encoded OID to a dot-separated string
console.log(asn1.derToOid(derOidBuffer));
// output: 1.2.840.113549.1.1.5

// validates that an ASN.1 object matches a particular ASN.1 structure and
// captures data of interest from that structure for easy access
var publicKeyValidator = {
  name: 'SubjectPublicKeyInfo',
  tagClass: asn1.Class.UNIVERSAL,
  type: asn1.Type.SEQUENCE,
  constructed: true,
  captureAsn1: 'subjectPublicKeyInfo',
  value: [{
    name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
    tagClass: asn1.Class.UNIVERSAL,
    type: asn1.Type.SEQUENCE,
    constructed: true,
    value: [{
      name: 'AlgorithmIdentifier.algorithm',
      tagClass: asn1.Class.UNIVERSAL,
      type: asn1.Type.OID,
      constructed: false,
      capture: 'publicKeyOid'
    }]
  }, {
    // subjectPublicKey
    name: 'SubjectPublicKeyInfo.subjectPublicKey',
    tagClass: asn1.Class.UNIVERSAL,
    type: asn1.Type.BITSTRING,
    constructed: false,
    value: [{
      // RSAPublicKey
      name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
      tagClass: asn1.Class.UNIVERSAL,
      type: asn1.Type.SEQUENCE,
      constructed: true,
      optional: true,
      captureAsn1: 'rsaPublicKey'
    }]
  }]
};

var capture = {};
var errors = [];
if(!asn1.validate(
  publicKeyValidator, subjectPublicKeyInfo, validator, capture, errors)) {
  throw 'ASN.1 object is not a SubjectPublicKeyInfo.';
}
// capture.subjectPublicKeyInfo contains the full ASN.1 object
// capture.rsaPublicKey contains the full ASN.1 object for the RSA public key
// capture.publicKeyOid only contains the value for the OID
var oid = asn1.derToOid(capture.publicKeyOid);
if(oid !== pki.oids['rsaEncryption']) {
  throw 'Unsupported OID.';
}

// pretty print an ASN.1 object to a string for debugging purposes
asn1.prettyPrint(object);

Message Digests

Provides SHA-1 message digests.

Examples

var md = forge.md.sha1.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Provides SHA-256 message digests.

Examples

var md = forge.md.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592

Provides SHA-384 message digests.

Examples

var md = forge.md.sha384.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1

Provides SHA-512 message digests.

Examples

// SHA-512
var md = forge.md.sha512.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6

// SHA-512/224
var md = forge.md.sha512.sha224.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37

// SHA-512/256
var md = forge.md.sha512.sha256.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d

Provides MD5 message digests.

Examples

var md = forge.md.md5.create();
md.update('The quick brown fox jumps over the lazy dog');
console.log(md.digest().toHex());
// output: 9e107d9d372bb6826bd81d3542a419d6

Provides HMAC w/any supported message digest algorithm.

Examples

var hmac = forge.hmac.create();
hmac.start('sha1', 'Jefe');
hmac.update('what do ya want for nothing?');
console.log(hmac.digest().toHex());
// output: effcdf6ae5eb2fa2d27416d5f184df9c259a7c79

Utilities

Provides an API for generating large, random, probable primes.

Examples

// generate a random prime on the main JS thread
var bits = 1024;
forge.prime.generateProbablePrime(bits, function(err, num) {
  console.log('random prime', num.toString(16));
});

// generate a random prime using Web Workers (if available, otherwise
// falls back to the main thread)
var bits = 1024;
var options = {
  algorithm: {
    name: 'PRIMEINC',
    workers: -1 // auto-optimize # of workers
  }
};
forge.prime.generateProbablePrime(bits, options, function(err, num) {
  console.log('random prime', num.toString(16));
});

Provides a Fortuna-based cryptographically-secure pseudo-random number generator, to be used with a cryptographic function backend, e.g. AES. An implementation using AES as a backend is provided. An API for collecting entropy is given, though if window.crypto.getRandomValues is available, it will be used automatically.

Examples

// get some random bytes synchronously
var bytes = forge.random.getBytesSync(32);
console.log(forge.util.bytesToHex(bytes));

// get some random bytes asynchronously
forge.random.getBytes(32, function(err, bytes) {
  console.log(forge.util.bytesToHex(bytes));
});

// collect some entropy if you'd like
forge.random.collect(someRandomBytes);
jQuery().mousemove(function(e) {
  forge.random.collectInt(e.clientX, 16);
  forge.random.collectInt(e.clientY, 16);
});

// specify a seed file for use with the synchronous API if you'd like
forge.random.seedFileSync = function(needed) {
  // get 'needed' number of random bytes from somewhere
  return fetchedRandomBytes;
};

// specify a seed file for use with the asynchronous API if you'd like
forge.random.seedFile = function(needed, callback) {
  // get the 'needed' number of random bytes from somewhere
  callback(null, fetchedRandomBytes);
});

// register the main thread to send entropy or a Web Worker to receive
// entropy on demand from the main thread
forge.random.registerWorker(self);

// generate a new instance of a PRNG with no collected entropy
var myPrng = forge.random.createInstance();

Provides queuing and synchronizing tasks in a web application.

Examples

// TODO

Provides utility functions, including byte buffer support, base64, bytes to/from hex, zlib inflate/deflate, etc.

Examples

// encode/decode base64
var encoded = forge.util.encode64(str);
var str = forge.util.decode64(encoded);

// encode/decode UTF-8
var encoded = forge.util.encodeUtf8(str);
var str = forge.util.decodeUtf8(encoded);

// bytes to/from hex
var bytes = forge.util.hexToBytes(hex);
var hex = forge.util.bytesToHex(bytes);

// create an empty byte buffer
var buffer = forge.util.createBuffer();
// create a byte buffer from raw binary bytes
var buffer = forge.util.createBuffer(input, 'raw');
// create a byte buffer from utf8 bytes
var buffer = forge.util.createBuffer(input, 'utf8');

// get the length of the buffer in bytes
buffer.length();
// put bytes into the buffer
buffer.putBytes(bytes);
// put a 32-bit integer into the buffer
buffer.putInt32(10);
// buffer to hex
buffer.toHex();
// get a copy of the bytes in the buffer
bytes.bytes(/* count */);
// empty this buffer and get its contents
bytes.getBytes(/* count */);

// convert a forge buffer into a Node.js Buffer
// make sure you specify the encoding as 'binary'
var forgeBuffer = forge.util.createBuffer();
var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary');

// convert a Node.js Buffer into a forge buffer
// make sure you specify the encoding as 'binary'
var nodeBuffer = Buffer.from('CAFE', 'hex');
var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary'));

Provides logging to a javascript console using various categories and levels of verbosity.

Examples

// TODO

The flash README provides details on rebuilding the optional Flash component used for networking. It also provides details on Policy Server support.

Security Considerations

When using this code please keep the following in mind:

  • Cryptography is hard. Please review and test this code before depending on it for critical functionality.
  • The nature of JavaScript is that execution of this code depends on trusting a very large set of JavaScript tools and systems. Consider runtime variations, runtime characteristics, runtime optimization, code optimization, code minimization, code obfuscation, bundling tools, possible bugs, the Forge code itself, and so on.
  • If using pre-built bundles from NPM, another CDN, or similar, be aware someone else ran the tools to create those files.
  • Use a secure transport channel such as TLS to load scripts and consider using additional security mechanisms such as Subresource Integrity script attributes.
  • Use "native" functionality where possible. This can be critical when dealing with performance and random number generation. Note that the JavaScript random number algorithms should perform well if given suitable entropy.
  • Understand possible attacks against cryptographic systems. For instance side channel and timing attacks may be possible due to the difficulty in implementing constant time algorithms in pure JavaScript.
  • Certain features in this library are less susceptible to attacks depending on usage. This primarily includes features that deal with data format manipulation or those that are not involved in communication.

Library Background

Contact

Donations

Financial support is welcome and helps contribute to futher development:

forge's People

Contributors

aljones15 avatar andersk avatar cadorn avatar davidlehn avatar dhensby avatar dlongley avatar emirotin avatar evanj avatar evgenus avatar fxa avatar hiddentao avatar jduncanator avatar kevincennis avatar lautaroscytl avatar leoselig avatar linuxwolf avatar mattcollier avatar mgcrea avatar msporny avatar mundry avatar renzenicolai avatar siebertm avatar stardast avatar stesie avatar tomds avatar troyfactor4 avatar vbuch avatar vijayp avatar waynesan avatar zachmart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

forge's Issues

Multiple Handshake Messages parsing issue

The message to parse is something like :

16 handshake
0301 version
0d26 length

02 server hello
000026 length (38)
0301 TLS 1.0
515ab0c2 unix time
06d6cb5103d5710221030375ef1e794c6299539bb36f77a70ce331b8 random (28)
00 session id length
002f cipher suite
00 compression method

followed by:

0b certificate
000cf4 length
etc

If we look at the parse methods, there is a length parameter that does not seem to be really used.

For example : parseHelloMessage in the above case will not detect the certificate message and continue parsing the server hello message thinking that it is parsing extensions.

Maybe there should be a check like if (record.fragment.read===length) {don't continue parsing}

The issue might be global, so I prefer to ask.

forge.pkcs5.pbkdf2

Hi,

I just noticed forge.pkcs5.pbkdf2 is giving different results than other crypto libs on this tests:

Forge:

var password = 'password';
var salt = '4bcda0d1c689fe465c5b8a817f0ddf3d';
var md = forge.md.sha256.create();
var dkey = forge.pkcs5.pbkdf2(password, salt, 1000, 48, md);
console.log(forge.util.bytesToHex(dkey));
// 9da8a5f4ae605f35e82e5beac5f362df15c4255d88f738d641466a4107f99702f35aa5536799e625bab4db080b11f206

sjcl (default using sha256)

var password = 'password';
var salt = '4bcda0d1c689fe465c5b8a817f0ddf3d';
var bitsalt = sjcl.codec.utf8String.toBits(salt);
var keyhexbits = sjcl.misc.pbkdf2(password, bitsalt, 1000, 384);
console.log(sjcl.codec.hex.fromBits(keyhexbits));
// 9da8a5f4ae605f35e82e5beac5f362df15c4255d88f738d641466a4107f9970238e768e72af29ac89a1b16ff277b31d2

CryptoJS

var password = 'password';
var salt = '4bcda0d1c689fe465c5b8a817f0ddf3d';
var key = CryptoJS.PBKDF2(password, salt, { keySize: 384/32, hasher: CryptoJS.algo.SHA256, iterations: 1000 });
console.log(key.toString());
// 9da8a5f4ae605f35e82e5beac5f362df15c4255d88f738d641466a4107f9970238e768e72af29ac89a1b16ff277b31d2
sjcl match with CryptoJS, Forge match only the first 32 bytes:

9da8a5f4ae605f35e82e5beac5f362df15c4255d88f738d641466a4107f99702

PEM encapsulated header fields

Using the private key below, I get the following error:

Uncaught Invalid PEM format pki.js:1019
pki.pemToDer pki.js:1019
pki.encryptedPrivateKeyFromPem pki.js:3071
pki.decryptRsaPrivateKey pki.js:3105

The key was generated with openssl through Ruby, which includes encapsulated header fields defined in RFC 1421 in the pem file.

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,908A2D85694C80A7BF001D69B48F8974

zZpNVpTuggEHTk0H1RmS9667k7p/mM/3RIyAFx2u82oNdbjI0vj75ug1M6DZHIKT
GK372Xg1ND6Xbz3N7A9Vuhu2ETmd0jv4Knp+5NNfkCpqJit//OeWfCCLLz5WlQ4Z
2YjDl57XEihJXUYq4LABJn95udFUcrKOJcj5Sz6PaiHHKOyx/KC0sil4d+AdIzL6
W4KLVygT5iymFGcWyZcW1GdVeQJAhE2qKfd3HtgvCDNPdBuaTxdGv12KFkiwvM4z
5KzIhBxTSEeFr1Ef8pzi7VRR7ip+0+d9xzNZXqlMt6FfurA+MCBF2Y1mzwyiu0EB
ka9xXQnJPTzkaPGmVDtJdxrbIWGHcYVlC+21Vo+lxJyPBqvMdAczRBslIAkmKW53
Lx/dEL3fKcbFPFoLANosLEk+pAE/Tw+ZGMwDPKrpSFdm3zwfiSlYjJexGxTQXRnp
Ot5bt463Bfio/V7hJDDuKSB65VttuqHJxo3FDNILEd/zu2T9G1mzloZWrUrMQ/nD
gHiYVi4YMNv2FEZmqNSyUMXzOnLxD+I70YArsJ9q21wNTd5cWfnRqh2osZsdeUyw
UTmMH3fBZE/L0JlnAOSsDWjUhvoPY9R/YQ8JjXrSw5BplHVpB6/UEV6MMSDFcbCg
VAo7dze9WWkRTRR+XHPmoyViik5ydJk63Hgf7JaDje2WsGUxGy011FHqsWHMb2MC
zWC2AKYzMwFSoSwwkRu0xMyghq0kR1LagX/qjMIPTcHzJ57EzXs8eZyt6Mw5cjXh
eDPADOZSLNQ/E9YHJNsMQ1/lM3YBakxGvPUBwSQ9ZTpthrcMyZo/9YVnlL6iW1mv
-----END RSA PRIVATE KEY-----

The passphrase for this key is testing if you want to use it for a test case.

Can't access gmail's imap service via forge TLS

I would like to add TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA support.

Could you provide some guidance as to where I might start, and how I might do this?

Update:

I actually need TLS_RSA_WITH_RC4_128_SHA (RC4-SHA) support, as indicated by openssl s_client dump here:

New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
Session-ID: XXXXX
Session-ID-ctx:
Master-Key: ZZZZZ
Key-Arg : None
Start Time: 1370720335
Timeout : 300 (sec)

This is to create a secure connection to gmail's imap service.

asn1.js - DER encoding of BMPSTRING objects

asn1.toDer() currently does not handle the type BMPSTRING correctly. The following patch adds this functionality (compare to the implementation of fromDer():

--- asn1.js.orig        2013-03-06 16:44:24.772635500 +0100
+++ asn1.js.new 2013-03-06 16:43:41.229875000 +0100
@@ -415,7 +415,11 @@
   }
   // use asn1.value directly
   else {
-    value.putBytes(obj.value);
+    if(obj.type === asn1.Type.BMPSTRING) {
+        for(var i = 0; i < obj.value.length; i ++)
+            value.putInt16(obj.value.charCodeAt(i));
+    } else
+        value.putBytes(obj.value);
   }

   // add tag byte

How to generate PKCS#7 pem from CRTs

I am looking for an alternative to this:

openssl crl2pkcs7 -nocrl -certfile file1.crt -certfile file2.crt

I tried with

  var p7 = forge.pkcs7.createEnvelopedData();
  p7.addRecipient(cert1);
  p7.addRecipient(cert2);
//not sure about this
  p7.content = forge.util.createBuffer('');
  p7.encrypt();
//////
  console.log(forge.pkcs7.messageToPem(p7));

But I am not sure if is the very same thing.

Regards

P12 Keystore aliases - question

Hi,

I'm using Forge to import existing P12 keystores as well as generate new ones. As part of our business logic we required the alias on the safeBags, e.g. the Friendly Name. With our existing implementation, BouncyCastle, this is available as an attribute on each safeBag alongside the localKeyId.

However, with Forge it seems it's only possible to supply the localKeyId, which I've done (code below) but when I reopen the keyStore it appears to be stored in some kind of binary format. I can't work out how to decrypt the friendlyName from this.

    var options = {
            localKeyId : myUtil.toHexAndUpperCase(aliasName),
            generateLocalKeyId : false
    };
    //Create the PKCS#12
    var pkcs12 = forge.pkcs12.toPkcs12Asn1(privateKey, x509Cert, encodedPassword(password), options);

There are references to friendlyName in the code comments, am I missing something or does Forge not support this?

Thanks,
Kevin

Problem validation certificates cert chain with differents oids

If I try to validate the certificate chain from a ca with different oid that its child, the validation fail.
The problem is that the parent and child use different oids.

This is my test:
it("validate certificate chain", function() {
var ca = forge.pki.certificateFromPem(CA_CERTIFICATE);
var cer = forge.pki.certificateFromPem(CHILD_CERTIFICATE);
// verify certificate
var caStore = forge.pki.createCaStore();
caStore.addCertificate(ca);
try {
forge.pki.verifyCertificateChain(caStore, [ cer ],
function(vfd, depth, chain) {
expect(vfd).toBe(true);
return vfd;
});
} catch (e) {
expect(false).toBe(true);
}
});

To solve this problem, in the function cert.verify in pki.js, I've modifed this line

var oid = oids[cert.signatureOid];

for this one

oid = oids[child.signatureOid];

The solution is get the oid from the child, not from the parent.

IE11 getRandomValues()

be aware, that IE11 has cryptographic correct random values, but uses
window.msCrypto.getRandomValues() instead of window.crypto.getRandomValues().
Microsoft released yesterday the IE11 preview for Windows 7, so we will get a lot of IE11 users, when MS pushes the IE11 with an window update in some weeks/months.

Could you please modify random.js and prng.js to support msCrypto.getRandomValues().
Be aware, I get a QuotaExceededError, when calling getRandomValues() with an array of about 100kb in IE11 (with Chrome I had no problem with more than 1mb)

AMD (requirejs/define) compatibility

Looks like the JS modules are intended to be loaded via <script> tags or in nodejs.

I need to load them via requirejs which uses define() from the AMD spec to declare modules.

Any objections to me updating the module wrapping to also support loading via AMD compatible loaders?

I propose the following wrapper (example for pki.js):

(function() {
var deps = {
  aes: './aes',
  asn1: './asn1',
  des: './des',
  md: './md',
  mgf: './mgf',
  pkcs5: './pbkdf2',
  pki: {
    oids: './oids',
    rsa: './rsa'
  },
  pss: './pss',
  random: './random',
  rc2: './rc2',
  util: './util',
  jsbn: './jsbn',
  pkcs12: './pkcs12'
};
var name = "pki";
function init(forge) {


var pki = forge.pki;

...


}
/* ########## Begin module wrapper ########## */
var cjsDefine = null;
if (typeof define !== 'function') {
  // CommonJS -> AMD
  if (typeof exports === "object") {
    cjsDefine = function(ids, init) {
      module.exports = init.apply(null, ids.map(function(id) {
        return require(id);
      }));
    }
  } else
  // <script>
  {
    var forge = window.forge = window.forge || {};
    forge[name] = forge[name] || {};
    init(forge);
  }
}
// AMD
if (cjsDefine || typeof define === 'function') {
  var ids = [];
  var assigns = [];
  function forEachDep(path, deps) {
    function assign(path) {
      var index = ids.length;
      ids.push(deps[path[path.length-1]]);
      assigns.push(function(forge, args) {
        var id;
        while(path.length > 1) {
          id = path.shift();
          forge = forge[id] = forge[id] || {};
        }
        forge[path[0]] = args[index];
      });
    }
    for (var alias in deps) {
      if (typeof deps[alias] === "string") {
        assign(path.concat(alias));
      } else {
        forEachDep(path.concat(alias), deps[alias]);
      }
    }
    return forge;
  }
  forEachDep([], deps);
  (cjsDefine || define)(ids, function() {
    var args = arguments;
    var forge = {};
    assigns.forEach(function(assign) {
      assign(forge, args);
    });
    var exports = forge[name] = forge[name] || {};
    init(forge);
    return exports;
  });
}
})();

I know this is ugly and verbose but seems to be the only way to get compatibility without requiring a loader script nor messing with the existing namespaces.

The above works on nodejs and in the browser via requirejs.

I have not tested loading via <script> tag.

Any other suggestions?

Client cert chain not created correctly in tls.createCertificate

In tls.js (specifically tls.createCertificate), the client cert chain appears to not be created correctly if there are multiple certificates. Instead of passing one cert to pemToDer in each pass it uses the entire chain

for(var i = 0; i < cert.length; ++i) {
var der = forge.pki.pemToDer(cert);
...
}

should be (changing cert to cert[i])

for(var i = 0; i < cert.length; ++i) {
var der = forge.pki.pemToDer(cert[i]);
...
}

Here is the currently relevant line

Separate encryption library

Have you thought about separating out the various algorithms into a standalone library/libraries? It would be awesome if each algorithm had its own project and package each exposing a uniform interface thus allowing for selective inclusion. Currently there is a ton of encryption libraries and each one is just a featureless blob of algorithms.

Any project that makes use of encryption just includes a file or two, slowly growing their own internal crypto library. It would be awesome if we could just add a git submodule with AES only for example.

Also, if you're interested in speed, asm.js compiled code is just 2x slower than native.

RSA encrypt/decrypt issue

Given:

function encryptWithPublicKey(publicKey, data) {
    return UTIL.encode64(PKI.rsa.encrypt(data, publicKey, true));
}
function decryptWithPrivateKey(privateKey, data) {
    return PKI.rsa.decrypt(UTIL.decode64(data), privateKey, false, false);
}

When I run:

var original = "73031ebfe0805ae6f106ffa5a3ec974861a6fc509df7ef6832741fd905cfad19";
var encrypted = encryptWithPublicKey(publicKey, original);
var decrypted = decryptWithPrivateKey(privateKey, encrypted);

decrypted is set to 73031ebfe0805ae6f106ffa5a3ec974861a6fc509df7ef6832741fd905cfad19 but decrypted.length is 129 (not 64 as original.length).

decrypted as char code array is [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 51, 48, 51, 49, 101, 98, 102, 101, 48, 56, 48, 53, 97, 101, 54, 102, 49, 48, 54, 102, 102, 97, 53, 97, 51, 101, 99, 57, 55, 52, 56, 54, 49, 97…].

Looks like there is a bunch of 0 padding. How do I get rid of that using a different flag or is this a bug?

Remark on initModule

Hi again,

In older versions, you were checking window.

I think it was better because now it's not possible without hooking inside node.js to run the same code inside a browser and inside node.js (when you don't want to use node.js's modules, it can look strange but this is what I am doing).

To be clear: it seems more clean to define a window var on a server environment if you want to keep the code as it is inside the browser rather than destroying process properties, module.exports, etc to do so, which have of course side effects on other parts of the code that are using node.

Regards,

Aymeric

Openssl compatibility

Is RSA-OAEP compatible with openssl's version? I tried the following:

echo "deadbeef" | openssl rsautl -inkey ../tests/crypto/rsa_priv.pem -encrypt | xxd -p

And stored the output. Then I tried to decrypt it:

var rsaPriv = forge.pki.privateKeyFromPem(RSA_PRIV_PEM);
var hexMsg = "34c4553591a93d3836d32ae6c4d28cddff226f6e" ...
var msg = forge.util.hexToBytes(hexMsg);
var plainText = rsaPriv.decrypt(msg, "RSA-OAEP");

where RSA_PRIV_PEM is the private key encoded in a Javascript string variable, and I get a "Invalid RSAES-OAEP padding" error when I try to decrypt it. I looked at the code for forge and the code for openssl's version of RSA-OAEP, and at first blush they seem the same, but I wanted to check here before digging into this in more detail.

Am I doing something obviously wrong, should this work, or was forge never designed to be compatible with the "rsautl" tool from openssl?

Thanks for your help!

pkcs7.js - findRecipient

In method pkcs7.js, method findRecipient, the serial number and attributes of the recipients in the PKCS#7 message are compared with those of a given certificate. However, it compares the attributes of the certificate subject and not the certificate issuer.

I believe that line 457, which currently is

   var sAttr = cert.subject.attributes;

should instead be

   var sAttr = cert.issuer.attributes; 

Public/private key encryption question

Hello everyone,

first of all, after looking around for encryption libraries, I found that forge is the ideal thing to go with. Great work!

However, I am fairly new to using encryption and I have some questions that are not entirely forge related, but I guess that chances are high to get the best answers on this way from experts.

What I am trying to achieve is a strong public/private key encryption between two nodes on the application level. After generating 2048 bit RSA key pairs this is what I am doing on the encryption side:

  • Generating a random 256 bit AES key with 256 bit IV
  • Encrypting the AES key+IV as RSAES-OAEP using the remote's public key
  • Encrypting the data using the AES key
  • Concatenating the RSA-encrypted AES key+IV with the AES-encrypted data
  • Signing it with the private key of the sender
        publicKey = pki.publicKeyFromPem(publicKey);
        privateKey = pki.privateKeyFromPem(privateKey);
        data = util.encodeUtf8(data);
        var key = random.getBytesSync(AES_BYTES);
        var iv = random.getBytesSync(AES_BYTES);
        var cipher = aes.createEncryptionCipher(key);
        cipher.start(util.createBuffer(iv));
        cipher.update(util.createBuffer(data));
        cipher.finish();
        var enc = publicKey.encrypt(key+iv, "RSA-OAEP")+cipher.output.getBytes();
        var md = sha1.create();
        md.update(enc, 'raw');
        return {
            "enc": enc,
            "sig": privateKey.sign(md)
        };

And on the decryption side:

  • Verifying the raw data against the signature and the remote's public key
  • Decrypting the RSA-encrypted AES key+IV part using the local private key
  • Decrypting the AES-encrypted data using the decrypted AES key+IV
        privateKey = pki.privateKeyFromPem(privateKey);
        publicKey = pki.publicKeyFromPem(publicKey);
        var enc = data["enc"];
        var sig = data["sig"];
        var md = sha1.create();
        md.update(enc, 'raw');
        var verified = publicKey.verify(md.digest().getBytes(), sig);
        var aesData = enc.substring(0,RSA_BYTES);
        enc = enc.substring(RSA_BYTES);
        aesData = privateKey.decrypt(aesData, "RSA-OAEP");
        var key = aesData.substring(0,AES_BYTES);
        var iv = aesData.substring(AES_BYTES);
        var cipher = aes.createDecryptionCipher(key);
        cipher.start(util.createBuffer(iv));
        cipher.update(util.createBuffer(enc));
        cipher.finish();
        return {
            "dec": util.decodeUtf8(cipher.output.getBytes()),
            "ver": verified
        };

Is this the right way to do it?

Cheers

Interest in a patch for RSA-OAEP?

I've been using Forge to provide me with cryptographic primitives so I can communicate with a program written in another programming language. This existing program uses RSA keys with OAEP padding, so I implement it, using forge's primitives. If I were to send a pull request, would you accept this into Forge itself? It isn't useful for SSL/TLS (so maybe that is a no), but it would be useful for others who might want to use Forge to implement other systems (which seems like a bonus to me).

Thanks!

`HMAC` output formatting

Using HMAC as follows:

function calcHmac(secret, string) {
    var hm = HMAC.create();
    hm.start('sha256', secret);
    hm.update(string);
    return hm.getMac();
}

I am looking for an output format like:

3B092F18640146103F5F3D19D97109E0AA305D0FD36F917E4AB68334CC7CCD7D

How would I derive this format from the output that hm provides?

Wrong Server Key Exchange message during Handshake

Apparently for future uses forge has included a Server Key Exchange message sent by the server to the client that is for now always empty, because only RSA is supported.

This empty message is systematically sent by forge's tls server and then refused by usual tls clients (browsers, node), the tls connexion fails.

A possible workaround is to comment rval.putByte and rval.putInt24 in tls.createServerKeyExchange

Building into a single file using require.js

Hi,

I saw that forge's modules are loaded conditionally making generating a single file through require build difficult, since require can't handle that:

http://requirejs.org/docs/optimization.html

The reason I need this is because browserify can't resolve the node style require() depencies either and my hope is that requiring a single module might do the trick.

Is there any way to bundle the entire library into a single module?

Thanks

RSA key generation: JSBN 2X faster than Forge?

We've found Forge is pretty slow for generating RSA keys (using Node or Chrome). As a result, I ended up timing both Forge and the original JSBN implementation, and I found that the original JSBN implementation seems to be about 2X faster.

Despite the comments in rsa.js indicating that key generation comes from the original JSBN implementation, the code actually looks fairly different (ignoring the fact that Forge can be used "incrementally"). Is this timing difference expected?

Here are a sample of 20 times (in ms) for generating 2048-bit RSA keys from both JSBN and Forge on my laptop (2 GHz Core i7) using Node. You'll see that while there is significant variation, JSBN is on average about 2X faster.

JSBN:
[3403, 5529, 6393, 1189, 767, 3898, 7517, 3410, 1049, 3038, 8201, 5142, 2418, 2329, 1923, 3308, 3070, 3326, 11834, 4712]
avg: 4122.8

Forge:
[11784, 5503, 4270, 8614, 12066, 25130, 17522, 3664, 20016, 2416, 3170, 11703, 13551, 5465, 9123, 5051, 4271, 10412, 6256, 1181]
avg: 9058.4

Reverse Certificate Chain Verification

In the current code, certificate chains are verified in the reverse order. The isIssuer call checks to make sure that the argument issued the current object. But these two are swapped when verifying certificates in tls.js. Replace line 4058 with:

      if(error === null && !cert.isIssuer(parent))

Can you please tell me if it is possible to accomplish what the below code is doing in openssl with your module?

Hi,

Can you please tell me if it is possible to accomplish what the below code is doing in openssl with your module?

I'm basically trying to use my own CA which I already created to create and sign a pk12 format compatible S/MIME certificate.

sudo openssl genrsa -out smime.key 2048
sudo openssl req -new -config ../openssl.cnf -key smime.key -out smime.csr
sudo openssl x509 -req -days 365 -in smime.csr -CA ../cacert.pem -CAkey ../private/cakey.pem -set_serial 1 -out smime.crt -setalias "Self Signed SMIME" -addtrust emailProtection -addreject clientAuth -addreject serverAuth -trustout -extfile ../openssl.cnf -extensions mail_ext
sudo openssl pkcs12 -export -in smime.crt -inkey smime.key -out smime.p12

Thank you in advance,
Tony Yustein

Publish to npm!

$ npm install node-forge --save
npm http GET https://registry.npmjs.org/node-forge
npm http 404 https://registry.npmjs.org/node-forge
npm ERR! 404 'node-forge' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it
npm ERR! 404
npm ERR! 404 Maybe try 'npm search forge'
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, or http url, or git url.

isIssuer bug

Another random bug with the implementation of isIssuer in pki.js. You increment 'i' which is the issuer's certificate rather than 'n' the index variable. Change line 1385 to:

     for(var n; rval && n < i.attributes.length; ++n)

We actually make initialization explicit and don't bother with the early exit on our version, but you probably don't have to go that far:

for(var n = 0; n < i.attributes.length; ++n)

forge.util.getQueryVariables breaks on malformed URL parameter names

I noticed a problem with forge.util.getQueryVariables():

Once this method is called to split a URL containing malformed parameter names, the method breaks and exposes native DOM objects.

Example:
/testurl.html?constructor=1&proto=2&valueOf=3

Affected Code:

      if(!(key in rval)) {
        rval[key] = [];
      }
      if(val !== null) {
        rval[key].push(unescape(val));
      }

The key value will be used as index for rval. rval is an empty literal - and with key being set to e.g. constructor, we will access the native object constructor - and not the URL parameter as one might expect. This causes a TypeError and the code-flow to stop:

TypeError: rval[key].push is not a function

rval[key].push(unescape(val));

It should be made sure, that the key value undergoes a number of checks before being used as index for the rval array - so no (let's call it) DoS happens via URL parameters.

Upload to NPM

Hey,
I just saw this library, and am very interested in using it with nodejs, but the fact that it's not in the NPM repository is a real deal breaker for me, as it makes it harder both for deployment and contributors to use it. It'd be cool if this could be uploaded to NPM.

publicKeyTo/FromPem issue

Hi,

The following public key pem:

-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBALRbNcv5vHKpaRvM53E/Dl4QGGO7CjhsHBld8v6Lgx4I2OGVvNCaAop4
rk2GfMmI48gnGEm+dtWAO7n4FEv9FgtV2xFMyBIQFud0y19Yr6pYfb4L9GIBNTCi
vT/+poRHuDHlIyMk636t0ep4iWKR468az514tKZfyWY8pUsk8apTAgMBAAE=
-----END RSA PUBLIC KEY-----

gives an error if we use publicKeyFromPem.

The same key (modulus: 8bd0a81286858b11f2856ac2f3efd8a5e538b2f851a4c02cc717c94ea52418c587cc69515e46b5381d17cd2299cb4c59fae38250898eab31c9b8acfcf02110a96f0f03442ecd34f1befb0604e1ffbb981848534da9f3f23795ed7df047730cb9dab711722ad6504b893cebda2de4b41123d667474a166eba3ead7df8267dda15) gives with publicKeyToPem:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCL0KgShoWLEfKFasLz79il5Tiy
+FGkwCzHF8lOpSQYxYfMaVFeRrU4HRfNIpnLTFn644JQiY6rMcm4rPzwIRCpbw8D
RC7NNPG++wYE4f+7mBhIU02p8/I3le198EdzDLnatxFyKtZQS4k869ot5LQRI9Zn
R0oWbro+rX34Jn3aFQIDAQAB
-----END PUBLIC KEY-----

But openssl for example does not decode the second one, so probably the first one is correct.

I am wondering if the issue is not affecting certificateToPem too (could it be?), since I am having quite a hard time to make something work while everything looks correct, the only thing that I did not check is to decode a certificate pem with something else than forge.

Regards

Aymeric

Standalone Browser JS file

Is it possible to build just one file that exposes forge at window.forge?

The build script doesn't run on my mac, doesn't find aclocal, so I couldn't find out if that already does that.

Or can I just concatenate files together as I wish?

Besides that I'm using browserify, which does static analysis on node code to get require calls and bundle it up for the server, but because of the multi module system support it can't do that.

Question: can I connect to SSL-enabled IMAP servers using this?

I've been trying to connect to imap.gmail.com:993 using this library and node's net.Socket and can't figure out why it's not working.

I get as far as this:

[socket] Connected to host.
[tls] (handshake) client -> server: /+cØ�ê`ô'1�¼PVE�H¦ú?o¢t`.µ�`Õû/5
[socket] server -> client: /
[socket] FIN packet received. Disconnecting...
[socket] Connection closed.

The [socket] output is from my Socket event handlers, and the [tls] output is from my forge.tls client object event handlers. When I call tls.handshake() 5 seconds after my socket is connected it tries to do the 'Client Hello' and fails. The server then kills the connection. Wireshark says the packet is malformed:

screen shot 2013-06-21 at 15 15 25

Here's a successful Client Hello when using the openssl command-line client:

screen shot 2013-06-21 at 15 14 57

Here is my TLS client instance:

var tls = forge.tls.createConnection({
  server: false,
  verify: function(connection, verified, depth, certs) {
    return true;
  },
  connected: function(connection) {
    console.log('[tls] connected');
  },
  tlsDataReady: function(connection) {
    var data = connection.tlsData.getBytes();
    console.log('[tls]' +
      (connection.handshaking ? ' (handshake)' : '') +
      ' client -> server: ' + data);
    socket.write(data);
  },
  dataReady: function(connection) {
    // clear data from the server is ready
    var data = connection.data.getBytes();
    console.log('[tls] server -> client: ' + data);
  },
  closed: function() {
    console.log('[tls] disconnected');
  },
  error: function(connection, error) {
    console.log('[tls] uh oh', error);
  }
});

Does anyone know what might be wrong here?

Add MAC to PKCS12 object creation

The lack of MacData on PKCS12 objects created by pkcs12.js causes issues when importing these objects into other tools. In the tests included with forge, you have to use openssl pkcs12 -nomacverify. Mac OS Keychain Access refuses to import the object at all.

The below patch adds MacData to all PKCS12 objects by default. It uses HMAC-SHA1 for the MAC, as specified in PKCS#12. The only thing that might be nice to make configurable is the number of iterations in the key derivation; it's currently set statically to 1024.

diff -rupN ./js/pkcs12.js ../../forge-master/js/pkcs12.js
--- ./js/pkcs12.js  2013-01-14 11:24:29.000000000 -0500
+++ ../../forge-master/js/pkcs12.js 2013-01-14 11:29:09.000000000 -0500
@@ -110,7 +110,9 @@ else if(typeof(module) !== 'undefined' &
       asn1: require('./pkcs7asn1')
     },
     pki: require('./pki'),
-    util: require('./util')
+    util: require('./util'),
+    random: require('./random'),
+    hmac: require('./hmac')
   };
   module.exports = forge.pkcs12 = {};
 }
@@ -741,6 +743,37 @@ p12.toPkcs12Asn1 = function(key, cert, p
     ])
   ]);

+  // MacData
+  var sha1 = forge.md.sha1.create();
+  var macSalt = new forge.util.ByteBuffer(forge.random.getBytes(sha1.blockLength));
+  var iter = 1024;
+  var key = p12.generateKey(password || "", macSalt, 3, iter, 20); // 160-bit key
+  var mac = forge.hmac.create();
+  mac.start(sha1, key);
+  mac.update( asn1.toDer(safe).getBytes()); 
+  var macValue = mac.getMac();
+  var macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+    // mac DigestInfo
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+      // digestAlgorithm
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
+        // algorithm = SHA-1
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, 
+          asn1.oidToDer(oids['sha1']).getBytes()),
+        // parameters = Null
+        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
+      ]),
+      // digest
+      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macValue.getBytes())
+    ]),
+    // macSalt OCTET STRING
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()),
+    // iterations INTEGER (XXX: Only support iter < 65536)
+    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, 
+      forge.util.hexToBytes( iter.toString(16) )
+    )
+  ]);
+
   // PFX
   return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
     // version (3)
@@ -758,7 +791,8 @@ p12.toPkcs12Asn1 = function(key, cert, p
           asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
           asn1.toDer(safe).getBytes())
       ])
-    ])
+    ]),
+    macData
   ]);
 };

RSA padding pseudo random numbers

Hi Dave,

Is I have suggested, you substitued your Math.random() call with:
var padBytes = forge.random.getBytes(padNum);
for(var i = 0; i < padNum; ++i) {
eb.putByte(Math.max(1, padBytes.charCodeAt(i)));
}

This is nearly perfect, only the number 1 is twice as often as the others.
Please replace the block above with
variant1
var padBytes = forge.random.getBytes(padNum);
for(var i = 0; i < padNum; ) {
var byte = forge.random.getBytes(1).charCodeAt(0);
if (byte !== 0) {
eb.putByte(byte);
i += 1;
}
}
or variant2
while (padNum > 0) {
var numZeros = 0;
var padBytes = forge.random.getBytes(padNum);
for(var i = 0; i < padNum; ++i) {
var byte = padBytes.charCodeAt(i);
if (byte === 0) {
numZeros += 1;
}
else {
eb.putByte(byte);
}
}
padNum = numZeros;
}
(variant1 is slower, but has less code)
It might be paranoia mode to enforce uniform distributed pseudo random numbers, but, you know, what was paranoia yesterday, is nowadays standard

Franz

Certificate chain with 1 certificate (signed by ca cert in store) fails

If I pass a single certificate in the chain to a client, and that certificate is not a ca certificate but has been signed by a ca certificate, present in the ca store, the verification process fails with 'Certificate basicConstraints indicates the certificate is not a CA'.

Should line 2409 in pki.js be changed to allow this?, so:

' if(!first || chain.length === 0) {'

becomes

'if (!first) {'

I suppose an alternative would be to pass in the ca certificate as part of the chain, though passing multiple certificates has not yet been implemented (in tls.js, function on line 3054)

What is the correct solution to this problem?

Public key from private (RSA)

When I generate an RSA key pair:

var pair = RSA.generateKeyPair(size, 0x10001);

And keep the private key as:

var pk = ASN1.toDer(PKI.privateKeyToAsn1(pair.privateKey)).getBytes()

How can I derive the public key from pk?

I am assuming I can get the public key for a private key in the first place?

Cannot choose the hash algorithm on RSA-OAEP

In rsa.js the scheme can only have values 'RSAES-PKCS1-v1_5', 'RSA-OAEP', 'RSAES-OAEP', 'RAW', 'NONE', 'NULL'

The oaep scheme is automatically calling forge.pkcs1.encode_rsa_oaep(key, m);

pkcs1.js has default hash algorithm set to sha-1. Therefore for me now the only option is to alter the pkcs1.js and set the default to sha-256 (which is good enough for now)

revision controlled .min.js files

I personally think the .min.js files should be left out of revision control. Just add instructions and a script to build them. Is that too much effort for users to handle? Would pre-built versioned downloads be better?

If they are left in, should they be updated with every single source changing commit? That would be a pain to do and cause repo bloat but I think could cause confusion if not done. If constant or per-release .min.js updating is the way forward, then please update the HACKING.md file to spell out the steps needed and when to perform them to keep the .min.js files correct.

Consolidate and write new tests using mocha

The tests in Forge are lacking and split up using several different libraries (and custom code). They need to be unified and able to be run in both a browser and node.js. We should move all of the tests over to using mocha and write new tests.

Fails on fragmented messages

When a POST needs to be fragmented, forge throws an error. This is because you accidentally try to call 'splice' on a string when there is no such function. You need to replace lines 3754-3761 in tls.js with:

     while(data.length > tls.MaxFragment)
     {
        records.push(tls.createRecord(
        {
           type: record.type,
           data: forge.util.createBuffer(data.slice(0, tls.MaxFragment))
        }));
    data = data.slice(tls.MaxFragment);
     }

[PKCS12] Generated PKCS12 DER unreadable in iOS, works with openssl CLI

Using the code from the test, I created a DER encoded ASN.1 PKCS#12 container. I chose 3des as the algorithm for compatibility, but also tested the other options.
Using the openssl CLI like this: openssl pkcs12 -info -in forgeGenerated.p12 the container is read without any problems. In iOS on the other hand, using SecPKCS12Import I get the error errSecDecode, meaning that the data could not be decoded.

To eliminate errors in the obj-c code as a cause, I also generated a PKCS#12 container with the same certificate and private key on the CLI with openssl. This file was decoded in iOS without any problems.

What could cause this?

When I compare the output of openssl pkcs12 -info -in forgeGenerated.p12 with openssl pkcs12 -info -in openSSLGenerated.p12 I notice a few differences:

  • the openssl-generated version specifies the encryption algos for the certificate bag and the schrouded keybag
  • the forge-generated version doesn't specify any algos

See output below:

openssl generated version:

MAC Iteration 2048
MAC verified OK
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
Bag Attributes
    localKeyID: 58 8C A3 ED 1B 3C 4C B1 56 39 97 E1 4F B4 93 16 12 DF 5E 19
subject=/C=NL/ST=a/L=a/O=a/OU=a/CN=a
issuer=/C=NL/ST=a/L=a/O=a/OU=a/CN=a
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
Bag Attributes
    localKeyID: 58 8C A3 ED 1B 3C 4C B1 56 39 97 E1 4F B4 93 16 12 DF 5E 19
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,9D7B97D293D9C389

...
-----END RSA PRIVATE KEY-----

Forge generated version:

MAC Iteration 2048
MAC verified OK
PKCS7 Data
Shrouded Keybag: Bag Attributes
    localKeyID: 9E 67 FF FC 03 DE DF C8 45 63 8C DE C3 32 32 64 A5 BE 0E 63
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,351D4D52DBD1E34D

...
-----END RSA PRIVATE KEY-----
PKCS7 Data
Certificate bag
Bag Attributes
    localKeyID: 9E 67 FF FC 03 DE DF C8 45 63 8C DE C3 32 32 64 A5 BE 0E 63
subject=/C=NL/ST=a/L=a/O=a/OU=a/CN=a
issuer=/C=NL/ST=a/L=a/O=a/OU=a/CN=a
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

Insufficient type checks using obj.constructor

The library uses a specific yet weak pattern for type checks in many places - for instance here (aes.js#832):

  // convert key integer array into byte buffer
  else if(key.constructor == Array &&
    (key.length == 16 || key.length == 24 || key.length == 32)) {

This check is not very reliable and can easily be circumvented:

x = {constructor: String};

console.log(x.constructor == String); // true
console.log(x instanceof String); // false

Is there a specific reason why these kinds of checks are being used? Why not make use of instanceof?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.