sshpk
=========

Parse, convert, fingerprint and use SSH keys (both public and private) in pure
node -- no `ssh-keygen` or other external dependencies.

Supports RSA, DSA, ECDSA (nistp-\*) and ED25519 key types, in PEM (PKCS#1, 
PKCS#8) and OpenSSH formats.

This library has been extracted from
[`node-http-signature`](https://github.com/joyent/node-http-signature)
(work by [Mark Cavage](https://github.com/mcavage) and
[Dave Eddy](https://github.com/bahamas10)) and
[`node-ssh-fingerprint`](https://github.com/bahamas10/node-ssh-fingerprint)
(work by Dave Eddy), with additions (including ECDSA support) by
[Alex Wilson](https://github.com/arekinath).

Install
-------

```
npm install sshpk
```

Examples
--------

```js
var sshpk = require('sshpk');

var fs = require('fs');

/* Read in an OpenSSH-format public key */
var keyPub = fs.readFileSync('id_rsa.pub');
var key = sshpk.parseKey(keyPub, 'ssh');

/* Get metadata about the key */
console.log('type => %s', key.type);
console.log('size => %d bits', key.size);
console.log('comment => %s', key.comment);

/* Compute key fingerprints, in new OpenSSH (>6.7) format, and old MD5 */
console.log('fingerprint => %s', key.fingerprint().toString());
console.log('old-style fingerprint => %s', key.fingerprint('md5').toString());
```

Example output:

```
type => rsa
size => 2048 bits
comment => foo@foo.com
fingerprint => SHA256:PYC9kPVC6J873CSIbfp0LwYeczP/W4ffObNCuDJ1u5w
old-style fingerprint => a0:c8:ad:6c:32:9a:32:fa:59:cc:a9:8c:0a:0d:6e:bd
```

More examples: converting between formats:

```js
/* Read in a PEM public key */
var keyPem = fs.readFileSync('id_rsa.pem');
var key = sshpk.parseKey(keyPem, 'pem');

/* Convert to PEM PKCS#8 public key format */
var pemBuf = key.toBuffer('pkcs8');

/* Convert to SSH public key format (and return as a string) */
var sshKey = key.toString('ssh');
```

Signing and verifying:

```js
/* Read in an OpenSSH/PEM *private* key */
var keyPriv = fs.readFileSync('id_ecdsa');
var key = sshpk.parsePrivateKey(keyPriv, 'pem');

var data = 'some data';

/* Sign some data with the key */
var s = key.createSign('sha1');
s.update(data);
var signature = s.sign();

/* Now load the public key (could also use just key.toPublic()) */
var keyPub = fs.readFileSync('id_ecdsa.pub');
key = sshpk.parseKey(keyPub, 'ssh');

/* Make a crypto.Verifier with this key */
var v = key.createVerify('sha1');
v.update(data);
var valid = v.verify(signature);
/* => true! */
```

Matching fingerprints with keys:

```js
var fp = sshpk.parseFingerprint('SHA256:PYC9kPVC6J873CSIbfp0LwYeczP/W4ffObNCuDJ1u5w');

var keys = [sshpk.parseKey(...), sshpk.parseKey(...), ...];

keys.forEach(function (key) {
	if (fp.matches(key))
		console.log('found it!');
});
```

Usage
-----

## Public keys

### `parseKey(data[, format = 'auto'[, options]])`

Parses a key from a given data format and returns a new `Key` object.

Parameters

- `data` -- Either a Buffer or String, containing the key
- `format` -- String name of format to use, valid options are:
  - `auto`: choose automatically from all below
  - `pem`: supports both PKCS#1 and PKCS#8
  - `ssh`: standard OpenSSH format,
  - `pkcs1`, `pkcs8`: variants of `pem`
  - `rfc4253`: raw OpenSSH wire format
  - `openssh`: new post-OpenSSH 6.5 internal format, produced by 
               `ssh-keygen -o`
- `options` -- Optional Object, extra options, with keys:
  - `filename` -- Optional String, name for the key being parsed 
                  (eg. the filename that was opened). Used to generate
                  Error messages
  - `passphrase` -- Optional String, encryption passphrase used to decrypt an
                    encrypted PEM file

### `Key.isKey(obj)`

Returns `true` if the given object is a valid `Key` object created by a version
of `sshpk` compatible with this one.

Parameters

- `obj` -- Object to identify

### `Key#type`

String, the type of key. Valid options are `rsa`, `dsa`, `ecdsa`.

### `Key#size`

Integer, "size" of the key in bits. For RSA/DSA this is the size of the modulus;
for ECDSA this is the bit size of the curve in use.

### `Key#comment`

Optional string, a key comment used by some formats (eg the `ssh` format).

### `Key#curve`

Only present if `this.type === 'ecdsa'`, string containing the name of the
named curve used with this key. Possible values include `nistp256`, `nistp384`
and `nistp521`.

### `Key#toBuffer([format = 'ssh'])`

Convert the key into a given data format and return the serialized key as
a Buffer.

Parameters

- `format` -- String name of format to use, for valid options see `parseKey()`

### `Key#toString([format = 'ssh])`

Same as `this.toBuffer(format).toString()`.

### `Key#fingerprint([algorithm = 'sha256'])`

Creates a new `Fingerprint` object representing this Key's fingerprint.

Parameters

- `algorithm` -- String name of hash algorithm to use, valid options are `md5`,
                 `sha1`, `sha256`, `sha384`, `sha512`

### `Key#createVerify([hashAlgorithm])`

Creates a `crypto.Verifier` specialized to use this Key (and the correct public
key algorithm to match it). The returned Verifier has the same API as a regular
one, except that the `verify()` function takes only the target signature as an
argument.

Parameters

- `hashAlgorithm` -- optional String name of hash algorithm to use, any
                     supported by OpenSSL are valid, usually including
                     `sha1`, `sha256`.

`v.verify(signature[, format])` Parameters

- `signature` -- either a Signature object, or a Buffer or String
- `format` -- optional String, name of format to interpret given String with.
              Not valid if `signature` is a Signature or Buffer.

### `Key#createDiffieHellman()`
### `Key#createDH()`

Creates a Diffie-Hellman key exchange object initialized with this key and all
necessary parameters. This has the same API as a `crypto.DiffieHellman`
instance, except that functions take `Key` and `PrivateKey` objects as
arguments, and return them where indicated for.

This is only valid for keys belonging to a cryptosystem that supports DHE
or a close analogue (i.e. `dsa`, `ecdsa` and `curve25519` keys). An attempt
to call this function on other keys will yield an `Error`.

## Private keys

### `parsePrivateKey(data[, format = 'auto'[, options]])`

Parses a private key from a given data format and returns a new
`PrivateKey` object.

Parameters

- `data` -- Either a Buffer or String, containing the key
- `format` -- String name of format to use, valid options are:
  - `auto`: choose automatically from all below
  - `pem`: supports both PKCS#1 and PKCS#8
  - `ssh`, `openssh`: new post-OpenSSH 6.5 internal format, produced by 
                      `ssh-keygen -o`
  - `pkcs1`, `pkcs8`: variants of `pem`
  - `rfc4253`: raw OpenSSH wire format
- `options` -- Optional Object, extra options, with keys:
  - `filename` -- Optional String, name for the key being parsed 
                  (eg. the filename that was opened). Used to generate
                  Error messages
  - `passphrase` -- Optional String, encryption passphrase used to decrypt an
                    encrypted PEM file

### `PrivateKey.isPrivateKey(obj)`

Returns `true` if the given object is a valid `PrivateKey` object created by a
version of `sshpk` compatible with this one.

Parameters

- `obj` -- Object to identify

### `PrivateKey#type`

String, the type of key. Valid options are `rsa`, `dsa`, `ecdsa`.

### `PrivateKey#size`

Integer, "size" of the key in bits. For RSA/DSA this is the size of the modulus;
for ECDSA this is the bit size of the curve in use.

### `PrivateKey#curve`

Only present if `this.type === 'ecdsa'`, string containing the name of the
named curve used with this key. Possible values include `nistp256`, `nistp384`
and `nistp521`.

### `PrivateKey#toBuffer([format = 'pkcs1'])`

Convert the key into a given data format and return the serialized key as
a Buffer.

Parameters

- `format` -- String name of format to use, valid options are listed under 
              `parsePrivateKey`. Note that ED25519 keys default to `openssh`
              format instead (as they have no `pkcs1` representation).

### `PrivateKey#toString([format = 'pkcs1'])`

Same as `this.toBuffer(format).toString()`.

### `PrivateKey#toPublic()`

Extract just the public part of this private key, and return it as a `Key`
object.

### `PrivateKey#fingerprint([algorithm = 'sha256'])`

Same as `this.toPublic().fingerprint()`.

### `PrivateKey#createVerify([hashAlgorithm])`

Same as `this.toPublic().createVerify()`.

### `PrivateKey#createSign([hashAlgorithm])`

Creates a `crypto.Sign` specialized to use this PrivateKey (and the correct
key algorithm to match it). The returned Signer has the same API as a regular
one, except that the `sign()` function takes no arguments, and returns a
`Signature` object.

Parameters

- `hashAlgorithm` -- optional String name of hash algorithm to use, any
                     supported by OpenSSL are valid, usually including
                     `sha1`, `sha256`.

`v.sign()` Parameters

- none

### `PrivateKey#derive(newType)`

Derives a related key of type `newType` from this key. Currently this is
only supported to change between `ed25519` and `curve25519` keys which are
stored with the same private key (but usually distinct public keys in order
to avoid degenerate keys that lead to a weak Diffie-Hellman exchange).

Parameters

- `newType` -- String, type of key to derive, either `ed25519` or `curve25519`

## Fingerprints

### `parseFingerprint(fingerprint[, algorithms])`

Pre-parses a fingerprint, creating a `Fingerprint` object that can be used to
quickly locate a key by using the `Fingerprint#matches` function.

Parameters

- `fingerprint` -- String, the fingerprint value, in any supported format
- `algorithms` -- Optional list of strings, names of hash algorithms to limit
                  support to. If `fingerprint` uses a hash algorithm not on
                  this list, throws `InvalidAlgorithmError`.

### `Fingerprint.isFingerprint(obj)`

Returns `true` if the given object is a valid `Fingerprint` object created by a
version of `sshpk` compatible with this one.

Parameters

- `obj` -- Object to identify

### `Fingerprint#toString([format])`

Returns a fingerprint as a string, in the given format.

Parameters

- `format` -- Optional String, format to use, valid options are `hex` and
              `base64`. If this `Fingerprint` uses the `md5` algorithm, the
              default format is `hex`. Otherwise, the default is `base64`.

### `Fingerprint#matches(key)`

Verifies whether or not this `Fingerprint` matches a given `Key`. This function
uses double-hashing to avoid leaking timing information. Returns a boolean.

Parameters

- `key` -- a `Key` object, the key to match this fingerprint against

## Signatures

### `parseSignature(signature, algorithm, format)`

Parses a signature in a given format, creating a `Signature` object. Useful
for converting between the SSH and ASN.1 (PKCS/OpenSSL) signature formats, and
also returned as output from `PrivateKey#createSign().sign()`.

A Signature object can also be passed to a verifier produced by
`Key#createVerify()` and it will automatically be converted internally into the
correct format for verification.

Parameters

- `signature` -- a Buffer (binary) or String (base64), data of the actual
                 signature in the given format
- `algorithm` -- a String, name of the algorithm to be used, possible values
                 are `rsa`, `dsa`, `ecdsa`
- `format` -- a String, either `asn1` or `ssh`

### `Signature.isSignature(obj)`

Returns `true` if the given object is a valid `Signature` object created by a
version of `sshpk` compatible with this one.

Parameters

- `obj` -- Object to identify

### `Signature#toBuffer([format = 'asn1'])`

Converts a Signature to the given format and returns it as a Buffer.

Parameters

- `format` -- a String, either `asn1` or `ssh`

### `Signature#toString([format = 'asn1'])`

Same as `this.toBuffer(format).toString('base64')`.

Errors
------

### `InvalidAlgorithmError`

The specified algorithm is not valid, either because it is not supported, or
because it was not included on a list of allowed algorithms.

Thrown by `Fingerprint.parse`, `Key#fingerprint`.

Properties

- `algorithm` -- the algorithm that could not be validated

### `FingerprintFormatError`

The fingerprint string given could not be parsed as a supported fingerprint
format, or the specified fingerprint format is invalid.

Thrown by `Fingerprint.parse`, `Fingerprint#toString`.

Properties

- `fingerprint` -- if caused by a fingerprint, the string value given
- `format` -- if caused by an invalid format specification, the string value given

### `KeyParseError`

The key data given could not be parsed as a valid key.

Properties

- `keyName` -- `filename` that was given to `Key#parse`
- `format` -- the `format` that was trying to parse the key
- `innerErr` -- the inner Error thrown by the format parser

### `KeyEncryptedError`

The key is encrypted with a symmetric key (ie, it is password protected). The
parsing operation would succeed if it was given the `passphrase` option.

Properties

- `keyName` -- `filename` that was given to `Key#parse`
- `format` -- the `format` that was trying to parse the key (currently can only
              be `"pem"`)

Friends of sshpk
----------------

 * [`sshpk-agent`](https://github.com/arekinath/node-sshpk-agent) is a library
   for speaking the `ssh-agent` protocol from node.js, which uses `sshpk`
