456 lines
13 KiB
Markdown
456 lines
13 KiB
Markdown
|
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`
|