Skip to content

Commit 9fb6b0f

Browse files
committed
xxx: add support for macos pkhh fingerprints
1 parent 8df0385 commit 9fb6b0f

File tree

5 files changed

+81
-6
lines changed

5 files changed

+81
-6
lines changed

bin/sshpk-conv

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ var options = [
3737
type: 'bool',
3838
help: 'Produce a private key as output'
3939
},
40+
{
41+
names: ['certificate', 'C'],
42+
type: 'bool',
43+
help: 'Expect a certificate as input, not a public/private key'
44+
},
4045
{
4146
names: ['derive', 'd'],
4247
type: 'string',
@@ -62,6 +67,11 @@ var options = [
6267
type: 'bool',
6368
help: 'With -F, generates an SPKI fingerprint instead of SSH'
6469
},
70+
{
71+
names: ['pkhh', 'P'],
72+
type: 'bool',
73+
help: 'With -F, generates a MacOS pkhh fingerprint instead of SSH'
74+
},
6575
{
6676
names: ['comment', 'c'],
6777
type: 'string',
@@ -161,6 +171,11 @@ if (require.main === module) {
161171
var f = sshpk.parseKey;
162172
if (opts.private)
163173
f = sshpk.parsePrivateKey;
174+
if (opts.certificate) {
175+
f = sshpk.parseCertificate;
176+
if (fmt === 'auto')
177+
fmt = 'pem';
178+
}
164179
try {
165180
var key = f(buf, fmt, parseOpts);
166181
} catch (e) {
@@ -176,6 +191,12 @@ if (require.main === module) {
176191
ifError(e);
177192
}
178193

194+
if (opts.certificate) {
195+
var cert = key;
196+
key = cert.subjectKey;
197+
key.comment = cert.subjects[0].toString();
198+
}
199+
179200
if (opts.derive)
180201
key = key.derive(opts.derive);
181202

@@ -198,13 +219,22 @@ if (require.main === module) {
198219
key.fingerprint('md5').toString());
199220
console.log('SPKI-SHA256 fingerprint: ' +
200221
key.fingerprint('sha256', 'spki').toString());
222+
console.log('MacOS pkhh fingerprint: ' +
223+
key.fingerprint('sha1', 'pkhh').toString());
201224
process.exit(0);
202225
return;
203226
}
204227

205228
if (opts.fingerprint) {
206229
var hash = opts.hash;
207-
var type = opts.spki ? 'spki' : 'ssh';
230+
var type = 'ssh';
231+
if (opts.spki)
232+
type = 'spki';
233+
if (opts.pkhh) {
234+
if (!hash)
235+
hash = 'sha1';
236+
type = 'pkhh';
237+
}
208238
var format = opts.outformat;
209239
var fp = key.fingerprint(hash, type).toString(format);
210240
outFile.write(fp);

lib/fingerprint.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,21 @@ function Fingerprint(opts) {
3232

3333
Fingerprint.prototype.toString = function (format) {
3434
if (format === undefined) {
35-
if (this.algorithm === 'md5' || this.hashType === 'spki')
35+
if (this.algorithm === 'md5' || this.hashType === 'spki' ||
36+
this.hashType === 'pkhh') {
3637
format = 'hex';
37-
else
38+
} else {
3839
format = 'base64';
40+
}
3941
}
4042
assert.string(format);
4143

4244
switch (format) {
4345
case 'hex':
4446
if (this.hashType === 'spki')
4547
return (this.hash.toString('hex'));
48+
if (this.hashType === 'pkhh')
49+
return (this.hash.toString('hex').toUpperCase());
4650
return (addColons(this.hash.toString('hex')));
4751
case 'base64':
4852
if (this.hashType === 'spki')

lib/formats/pkcs8.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = {
66
write: write,
77
writePkcs8: writePkcs8,
88
pkcs8ToBuffer: pkcs8ToBuffer,
9+
pkcs8PublicBitstringToBuffer: pkcs8PublicBitstringToBuffer,
910

1011
readECDSACurve: readECDSACurve,
1112
writeECDSACurve: writeECDSACurve
@@ -513,14 +514,17 @@ function writePkcs8RSAPublic(key, der) {
513514
der.endSequence();
514515

515516
der.startSequence(asn1.Ber.BitString);
517+
writePkcs8RSAPublicBitstring(key, der);
518+
der.endSequence();
519+
}
520+
521+
function writePkcs8RSAPublicBitstring(key, der) {
516522
der.writeByte(0x00);
517523

518524
der.startSequence();
519525
der.writeBuffer(key.part.n.data, asn1.Ber.Integer);
520526
der.writeBuffer(key.part.e.data, asn1.Ber.Integer);
521527
der.endSequence();
522-
523-
der.endSequence();
524528
}
525529

526530
function writePkcs8DSAPrivate(key, der) {
@@ -546,9 +550,13 @@ function writePkcs8DSAPublic(key, der) {
546550
der.endSequence();
547551

548552
der.startSequence(asn1.Ber.BitString);
553+
writePkcs8DSAPublicBitstring(key, der);
554+
der.endSequence();
555+
}
556+
557+
function writePkcs8DSAPublicBitstring(key, der) {
549558
der.writeByte(0x00);
550559
der.writeBuffer(key.part.y.data, asn1.Ber.Integer);
551-
der.endSequence();
552560
}
553561

554562
function writeECDSACurve(key, der) {
@@ -601,6 +609,25 @@ function writePkcs8ECDSAPublic(key, der) {
601609
der.writeBuffer(Q, asn1.Ber.BitString);
602610
}
603611

612+
function pkcs8PublicBitstringToBuffer(key) {
613+
var der = new asn1.BerWriter();
614+
switch (key.type) {
615+
case 'rsa':
616+
writePkcs8RSAPublicBitstring(key, der);
617+
break;
618+
case 'dsa':
619+
writePkcs8DSAPublicBitstring(key, der);
620+
break;
621+
case 'ecdsa':
622+
return (key.part.Q.data);
623+
case 'ed25519':
624+
return (key.part.A.data);
625+
default:
626+
throw (new Error('Unsupported key type: ' + key.type));
627+
}
628+
return (der.buffer);
629+
}
630+
604631
function writePkcs8ECDSAPrivate(key, der) {
605632
writeECDSACurve(key, der);
606633
der.endSequence();

lib/key.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ Key.prototype.hash = function (algo, type) {
118118
buf = this.toBuffer('rfc4253');
119119
} else if (type === 'spki') {
120120
buf = formats.pkcs8.pkcs8ToBuffer(this);
121+
} else if (type === 'pkhh') {
122+
buf = formats.pkcs8.pkcs8PublicBitstringToBuffer(this);
121123
} else {
122124
throw (new Error('Hash type ' + type + ' not supported'));
123125
}

test/fingerprint.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ var ED2_PEM = '-----BEGIN PUBLIC KEY-----\n' +
3838
'MCowBQYDK2VwAyEA+76Hlm7wYeCHyx5n0aK21NYd+luLS+Q2SwfvvJowqoM=\n' +
3939
'-----END PUBLIC KEY-----\n';
4040

41+
var PKHH_PUBKEY = 'ecdsa-sha2-nistp256 ' +
42+
'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBB0PaRc5Tc9C' +
43+
'w8A6RRqFa+IdHF5LUFK5hkv32GnMmk/Eo2eafy2Ihxp0YnEbyg096g5J8wV6dpXv' +
44+
'o2dXNYDoYjc=';
45+
4146
test('fingerprint', function(t) {
4247
var k = sshpk.parseKey(SSH_1024, 'ssh');
4348
var fp = k.fingerprint('md5').toString();
@@ -172,3 +177,10 @@ test('invalid fingerprints', function(t) {
172177
}, sshpk.FingerprintFormatError);
173178
t.end();
174179
});
180+
181+
test('pkhh fingerprint for macos', function (t) {
182+
var k = sshpk.parseKey(PKHH_PUBKEY, 'ssh');
183+
var f = k.fingerprint('sha1', 'pkhh');
184+
t.equal(f.toString(), '674C1B03D2A4885BB106E67D8A9BE82C078E27CB');
185+
t.end();
186+
});

0 commit comments

Comments
 (0)