sshpk
Advanced tools
Comparing version 1.8.3 to 1.9.0
@@ -53,2 +53,14 @@ // Copyright 2015 Joyent, Inc. | ||
function CertificateParseError(name, format, innerErr) { | ||
if (Error.captureStackTrace) | ||
Error.captureStackTrace(this, CertificateParseError); | ||
this.name = 'CertificateParseError'; | ||
this.format = format; | ||
this.certName = name; | ||
this.innerErr = innerErr; | ||
this.message = 'Failed to parse ' + name + ' as a valid ' + format + | ||
' format certificate: ' + innerErr.message; | ||
} | ||
util.inherits(CertificateParseError, Error); | ||
function KeyEncryptedError(name, format) { | ||
@@ -71,3 +83,4 @@ if (Error.captureStackTrace) | ||
SignatureParseError: SignatureParseError, | ||
KeyEncryptedError: KeyEncryptedError | ||
KeyEncryptedError: KeyEncryptedError, | ||
CertificateParseError: CertificateParseError | ||
}; |
@@ -10,2 +10,3 @@ // Copyright 2015 Joyent, Inc. | ||
var Key = require('./key'); | ||
var Certificate = require('./certificate'); | ||
var utils = require('./utils'); | ||
@@ -18,2 +19,3 @@ | ||
assert.object(opts, 'options'); | ||
assert.string(opts.type, 'options.type'); | ||
assert.buffer(opts.hash, 'options.hash'); | ||
@@ -27,2 +29,3 @@ assert.string(opts.algorithm, 'options.algorithm'); | ||
this.hash = opts.hash; | ||
this.type = opts.type; | ||
} | ||
@@ -50,7 +53,12 @@ | ||
Fingerprint.prototype.matches = function (key) { | ||
assert.object(key, 'key'); | ||
utils.assertCompatible(key, Key, [1, 0], 'key'); | ||
Fingerprint.prototype.matches = function (other) { | ||
assert.object(other, 'key or certificate'); | ||
if (this.type === 'key') { | ||
utils.assertCompatible(other, Key, [1, 0], 'key'); | ||
} else { | ||
utils.assertCompatible(other, Certificate, [1, 0], | ||
'certificate'); | ||
} | ||
var theirHash = key.hash(this.algorithm); | ||
var theirHash = other.hash(this.algorithm); | ||
var theirHash2 = crypto.createHash(this.algorithm). | ||
@@ -66,6 +74,15 @@ update(theirHash).digest('base64'); | ||
Fingerprint.parse = function (fp, enAlgs) { | ||
Fingerprint.parse = function (fp, options) { | ||
assert.string(fp, 'fingerprint'); | ||
var alg, hash; | ||
var alg, hash, enAlgs; | ||
if (Array.isArray(options)) { | ||
enAlgs = options; | ||
options = {}; | ||
} | ||
assert.optionalObject(options, 'options'); | ||
if (options === undefined) | ||
options = {}; | ||
if (options.enAlgs !== undefined) | ||
enAlgs = options.enAlgs; | ||
assert.optionalArrayOfString(enAlgs, 'algorithms'); | ||
@@ -113,3 +130,7 @@ | ||
return (new Fingerprint({algorithm: alg, hash: hash})); | ||
return (new Fingerprint({ | ||
algorithm: alg, | ||
hash: hash, | ||
type: options.type || 'key' | ||
})); | ||
}; | ||
@@ -116,0 +137,0 @@ |
@@ -45,2 +45,3 @@ // Copyright 2015 Joyent, Inc. | ||
der.readSequence(); | ||
var next = der.offset + der.length; | ||
@@ -50,2 +51,3 @@ var oid = der.readOID(); | ||
case '1.2.840.113549.1.1.1': | ||
der._offset = next; | ||
if (type === 'public') | ||
@@ -71,6 +73,2 @@ return (readPkcs8RSAPublic(der)); | ||
function readPkcs8RSAPublic(der) { | ||
// Null -- XXX this probably isn't good practice | ||
der.readByte(); | ||
der.readByte(); | ||
// bit string sequence | ||
@@ -99,5 +97,2 @@ der.readSequence(asn1.Ber.BitString); | ||
function readPkcs8RSAPrivate(der) { | ||
der.readByte(); | ||
der.readByte(); | ||
der.readSequence(asn1.Ber.OctetString); | ||
@@ -104,0 +99,0 @@ der.readSequence(); |
@@ -7,2 +7,4 @@ // Copyright 2015 Joyent, Inc. | ||
var PrivateKey = require('./private-key'); | ||
var Certificate = require('./certificate'); | ||
var Identity = require('./identity'); | ||
var errs = require('./errors'); | ||
@@ -20,2 +22,11 @@ | ||
parsePrivateKey: PrivateKey.parse, | ||
Certificate: Certificate, | ||
parseCertificate: Certificate.parse, | ||
createSelfSignedCertificate: Certificate.createSelfSigned, | ||
createCertificate: Certificate.create, | ||
Identity: Identity, | ||
identityFromDN: Identity.parseDN, | ||
identityForHost: Identity.forHost, | ||
identityForUser: Identity.forUser, | ||
identityForEmail: Identity.forEmail, | ||
@@ -27,3 +38,4 @@ /* errors */ | ||
SignatureParseError: errs.SignatureParseError, | ||
KeyEncryptedError: errs.KeyEncryptedError | ||
KeyEncryptedError: errs.KeyEncryptedError, | ||
CertificateParseError: errs.CertificateParseError | ||
}; |
@@ -120,2 +120,3 @@ // Copyright 2015 Joyent, Inc. | ||
var opts = { | ||
type: 'key', | ||
hash: this.hash(algo), | ||
@@ -122,0 +123,0 @@ algorithm: algo |
@@ -68,5 +68,13 @@ // Copyright 2015 Joyent, Inc. | ||
r = this.part.r.data; | ||
if (r[0] === 0x00) | ||
if (r.length > 20 && r[0] === 0x00) | ||
r = r.slice(1); | ||
s = this.part.s.data; | ||
if (s.length > 20 && s[0] === 0x00) | ||
s = s.slice(1); | ||
if ((this.hashAlgorithm && | ||
this.hashAlgorithm !== 'sha1') || | ||
r.length + s.length !== 40) { | ||
throw (new Error('OpenSSH only supports ' + | ||
'DSA signatures with SHA1 hash')); | ||
} | ||
buf.writeBuffer(Buffer.concat([r, s])); | ||
@@ -76,6 +84,4 @@ return (buf.toBuffer()); | ||
var inner = new SSHBuffer({}); | ||
r = this.part.r; | ||
if (r[0] === 0x00) | ||
r = r.slice(1); | ||
inner.writePart(r); | ||
r = this.part.r.data; | ||
inner.writeBuffer(r); | ||
inner.writePart(this.part.s); | ||
@@ -86,3 +92,5 @@ | ||
var curve; | ||
var sz = this.part.r.data.length * 8; | ||
if (r[0] === 0x00) | ||
r = r.slice(1); | ||
var sz = r.length * 8; | ||
if (sz === 256) | ||
@@ -89,0 +97,0 @@ curve = 'nistp256'; |
@@ -76,2 +76,10 @@ // Copyright 2015 Joyent, Inc. | ||
SSHBuffer.prototype.readInt64 = function () { | ||
assert.ok(this._offset + 8 < this._buffer.length, | ||
'buffer not long enough to read Int64'); | ||
var v = this._buffer.slice(this._offset, this._offset + 8); | ||
this._offset += 8; | ||
return (v); | ||
}; | ||
SSHBuffer.prototype.readChar = function () { | ||
@@ -110,2 +118,18 @@ var v = this._buffer[this._offset++]; | ||
SSHBuffer.prototype.writeInt64 = function (v) { | ||
assert.buffer(v, 'value'); | ||
if (v.length > 8) { | ||
var lead = v.slice(0, v.length - 8); | ||
for (var i = 0; i < lead.length; ++i) { | ||
assert.strictEqual(lead[i], 0, | ||
'must fit in 64 bits of precision'); | ||
} | ||
v = v.slice(v.length - 8, v.length); | ||
} | ||
while (this._offset + 8 > this._size) | ||
this.expand(); | ||
v.copy(this._buffer, this._offset); | ||
this._offset += 8; | ||
}; | ||
SSHBuffer.prototype.writeChar = function (v) { | ||
@@ -112,0 +136,0 @@ while (this._offset + 1 > this._size) |
{ | ||
"name": "sshpk", | ||
"version": "1.8.3", | ||
"version": "1.9.0", | ||
"description": "A library for finding and using SSH public keys", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
217
README.md
@@ -404,2 +404,202 @@ sshpk | ||
## Certificates | ||
`sshpk` includes basic support for parsing certificates in X.509 (PEM) format | ||
and the OpenSSH certificate format. This feature is intended to be used mainly | ||
to access basic metadata about certificates, extract public keys from them, and | ||
also to generate simple self-signed certificates from an existing key. | ||
Notably, there is no implementation of CA chain-of-trust verification, and no | ||
support for key usage restrictions (or other kinds of restrictions). Please do | ||
the security world a favour, and DO NOT use this code for certificate | ||
verification in the traditional X.509 CA chain style. | ||
### `parseCertificate(data, format)` | ||
Parameters | ||
- `data` -- a Buffer or String | ||
- `format` -- a String, format to use, one of `'openssh'`, `'pem'` (X.509 in a | ||
PEM wrapper), or `'x509'` (raw DER encoded) | ||
### `createSelfSignedCertificate(subject, privateKey[, options])` | ||
Parameters | ||
- `subject` -- an Identity, the subject of the certificate | ||
- `privateKey` -- a PrivateKey, the key of the subject: will be used both to be | ||
placed in the certificate and also to sign it (since this is | ||
a self-signed certificate) | ||
- `options` -- optional Object, with keys: | ||
- `lifetime` -- optional Number, lifetime of the certificate from now in | ||
seconds | ||
- `validFrom`, `validUntil` -- optional Dates, beginning and end of | ||
certificate validity period. If given | ||
`lifetime` will be ignored | ||
- `serial` -- optional Buffer, the serial number of the certificate | ||
### `createCertificate(subject, key, issuer, issuerKey[, options])` | ||
Parameters | ||
- `subject` -- an Identity, the subject of the certificate | ||
- `key` -- a Key, the public key of the subject | ||
- `issuer` -- an Identity, the issuer of the certificate who will sign it | ||
- `issuerKey` -- a PrivateKey, the issuer's private key for signing | ||
- `options` -- optional Object, with keys: | ||
- `lifetime` -- optional Number, lifetime of the certificate from now in | ||
seconds | ||
- `validFrom`, `validUntil` -- optional Dates, beginning and end of | ||
certificate validity period. If given | ||
`lifetime` will be ignored | ||
- `serial` -- optional Buffer, the serial number of the certificate | ||
### `Certificate#subjects` | ||
Array of `Identity` instances describing the subject of this certificate. | ||
### `Certificate#issuer` | ||
The `Identity` of the Certificate's issuer (signer). | ||
### `Certificate#subjectKey` | ||
The public key of the subject of the certificate, as a `Key` instance. | ||
### `Certificate#issuerKey` | ||
The public key of the signing issuer of this certificate, as a `Key` instance. | ||
May be `undefined` if the issuer's key is unknown (e.g. on an X509 certificate). | ||
### `Certificate#serial` | ||
The serial number of the certificate. As this is normally a 64-bit or wider | ||
integer, it is returned as a Buffer. | ||
### `Certificate#isExpired([when])` | ||
Tests whether the Certificate is currently expired (i.e. the `validFrom` and | ||
`validUntil` dates specify a range of time that does not include the current | ||
time). | ||
Parameters | ||
- `when` -- optional Date, if specified, tests whether the Certificate was or | ||
will be expired at the specified time instead of now | ||
Returns a Boolean. | ||
### `Certificate#isSignedByKey(key)` | ||
Tests whether the Certificate was validly signed by the given (public) Key. | ||
Parameters | ||
- `key` -- a Key instance | ||
Returns a Boolean. | ||
### `Certificate#isSignedBy(certificate)` | ||
Tests whether this Certificate was validly signed by the subject of the given | ||
certificate. Also tests that the issuer Identity of this Certificate and the | ||
subject Identity of the other Certificate are equivalent. | ||
Parameters | ||
- `certificate` -- another Certificate instance | ||
Returns a Boolean. | ||
### `Certificate#fingerprint([hashAlgo])` | ||
Returns the X509-style fingerprint of the entire certificate (as a Fingerprint | ||
instance). This matches what a web-browser or similar would display as the | ||
certificate fingerprint and should not be confused with the fingerprint of the | ||
subject's public key. | ||
Parameters | ||
- `hashAlgo` -- an optional String, any hash function name | ||
### `Certificate#toBuffer([format])` | ||
Serializes the Certificate to a Buffer and returns it. | ||
Parameters | ||
- `format` -- an optional String, output format, one of `'openssh'`, `'pem'` or | ||
`'x509'`. Defaults to `'x509'`. | ||
Returns a Buffer. | ||
### `Certificate#toString([format])` | ||
- `format` -- an optional String, output format, one of `'openssh'`, `'pem'` or | ||
`'x509'`. Defaults to `'pem'`. | ||
Returns a String. | ||
## Certificate identities | ||
### `identityForHost(hostname)` | ||
Constructs a host-type Identity for a given hostname. | ||
Parameters | ||
- `hostname` -- the fully qualified DNS name of the host | ||
Returns an Identity instance. | ||
### `identityForUser(uid)` | ||
Constructs a user-type Identity for a given UID. | ||
Parameters | ||
- `uid` -- a String, user identifier (login name) | ||
Returns an Identity instance. | ||
### `identityForEmail(email)` | ||
Constructs an email-type Identity for a given email address. | ||
Parameters | ||
- `email` -- a String, email address | ||
Returns an Identity instance. | ||
### `identityFromDN(dn)` | ||
Parses an LDAP-style DN string (e.g. `'CN=foo, C=US'`) and turns it into an | ||
Identity instance. | ||
Parameters | ||
- `dn` -- a String | ||
Returns an Identity instance. | ||
### `Identity#toString()` | ||
Returns the identity as an LDAP-style DN string. | ||
e.g. `'CN=foo, O=bar corp, C=us'` | ||
### `Identity#type` | ||
The type of identity. One of `'host'`, `'user'`, `'email'` or `'unknown'` | ||
### `Identity#hostname` | ||
### `Identity#uid` | ||
### `Identity#email` | ||
Set when `type` is `'host'`, `'user'`, or `'email'`, respectively. Strings. | ||
### `Identity#cn` | ||
The value of the first `CN=` in the DN, if any. | ||
Errors | ||
@@ -437,4 +637,4 @@ ------ | ||
- `keyName` -- `filename` that was given to `Key#parse` | ||
- `format` -- the `format` that was trying to parse the key | ||
- `keyName` -- `filename` that was given to `parseKey` | ||
- `format` -- the `format` that was trying to parse the key (see `parseKey`) | ||
- `innerErr` -- the inner Error thrown by the format parser | ||
@@ -449,6 +649,17 @@ | ||
- `keyName` -- `filename` that was given to `Key#parse` | ||
- `keyName` -- `filename` that was given to `parseKey` | ||
- `format` -- the `format` that was trying to parse the key (currently can only | ||
be `"pem"`) | ||
### `CertificateParseError` | ||
The certificate data given could not be parsed as a valid certificate. | ||
Properties | ||
- `certName` -- `filename` that was given to `parseCertificate` | ||
- `format` -- the `format` that was trying to parse the key | ||
(see `parseCertificate`) | ||
- `innerErr` -- the inner Error thrown by the format parser | ||
Friends of sshpk | ||
@@ -455,0 +666,0 @@ ---------------- |
167622
34
4141
667