|
|
@ -150,11 +150,11 @@ EC.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) { |
|
|
|
kty: jwk.kty |
|
|
|
, crv: jwk.crv |
|
|
|
, d: PEM._toUrlSafeBase64(d) |
|
|
|
//, dh: d
|
|
|
|
//, dh: toHex(d)
|
|
|
|
, x: PEM._toUrlSafeBase64(x) |
|
|
|
//, xh: x
|
|
|
|
//, xh: toHex(x)
|
|
|
|
, y: PEM._toUrlSafeBase64(y) |
|
|
|
//, yh: y
|
|
|
|
//, yh: toHex(y)
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
@ -187,11 +187,11 @@ EC.parsePkcs8 = function parseEcPkcs8(u8, jwk) { |
|
|
|
kty: jwk.kty |
|
|
|
, crv: jwk.crv |
|
|
|
, d: PEM._toUrlSafeBase64(d) |
|
|
|
//, dh: d
|
|
|
|
//, dh: toHex(d)
|
|
|
|
, x: PEM._toUrlSafeBase64(x) |
|
|
|
//, xh: x
|
|
|
|
//, xh: toHex(x)
|
|
|
|
, y: PEM._toUrlSafeBase64(y) |
|
|
|
//, yh: y
|
|
|
|
//, yh: toHex(y)
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
@ -219,9 +219,9 @@ EC.parseSpki = function parsePem(u8, jwk) { |
|
|
|
kty: jwk.kty |
|
|
|
, crv: jwk.crv |
|
|
|
, x: PEM._toUrlSafeBase64(x) |
|
|
|
//, xh: x
|
|
|
|
//, xh: toHex(x)
|
|
|
|
, y: PEM._toUrlSafeBase64(y) |
|
|
|
//, yh: y
|
|
|
|
//, yh: toHex(y)
|
|
|
|
}; |
|
|
|
}; |
|
|
|
EC.parsePkix = EC.parseSpki; |
|
|
@ -304,17 +304,20 @@ EC.pack = function (opts) { |
|
|
|
} |
|
|
|
var jwk = JSON.parse(JSON.stringify(opts.jwk)); |
|
|
|
var format = opts.format; |
|
|
|
if (opts.public || -1 !== [ 'spki', 'pkix' ].indexOf(format)) { |
|
|
|
if (opts.public || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) { |
|
|
|
jwk.d = null; |
|
|
|
} |
|
|
|
if ('EC' !== jwk.kty) { |
|
|
|
throw new Error("options.jwk.kty must be 'EC' for EC keys"); |
|
|
|
} |
|
|
|
if (!jwk.d) { |
|
|
|
if (!format || 'pkix' === format) { |
|
|
|
if (!format || -1 !== [ 'spki', 'pkix' ].indexOf(format)) { |
|
|
|
format = 'spki'; |
|
|
|
} else if ('spki' !== format) { |
|
|
|
throw new Error("options.format must be 'spki' for public EC keys"); |
|
|
|
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) { |
|
|
|
format = 'ssh'; |
|
|
|
} else { |
|
|
|
throw new Error("options.format must be 'spki' or 'ssh' for public EC keys, not (" |
|
|
|
+ typeof format + ") " + format); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (!format || 'sec1' === format) { |
|
|
@ -334,8 +337,12 @@ EC.pack = function (opts) { |
|
|
|
return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: EC.packSec1(jwk) }); |
|
|
|
} else if ('pkcs8' === format) { |
|
|
|
return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: EC.packPkcs8(jwk) }); |
|
|
|
} else { |
|
|
|
} else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) { |
|
|
|
return PEM.packBlock({ type: "PUBLIC KEY", bytes: EC.packSpki(jwk) }); |
|
|
|
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) { |
|
|
|
return EC.packSsh(jwk); |
|
|
|
} else { |
|
|
|
throw new Error("Sanity Error: reached unreachable code block with format: " + format); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
@ -386,6 +393,27 @@ EC.packSpki = function (jwk) { |
|
|
|
); |
|
|
|
}; |
|
|
|
EC.packPkix = EC.packSpki; |
|
|
|
EC.packSsh = function (jwk) { |
|
|
|
// Custom SSH format
|
|
|
|
var typ = 'ecdsa-sha2-nistp256'; |
|
|
|
var a = '32 35 36'; |
|
|
|
var b = '41'; |
|
|
|
var comment = jwk.crv + '@localhost'; |
|
|
|
if ('P-256' !== jwk.crv) { |
|
|
|
typ = 'ecdsa-sha2-nistp384'; |
|
|
|
a = '33 38 34'; |
|
|
|
b = '61'; |
|
|
|
} |
|
|
|
var x = toHex(base64ToUint8(urlBase64ToBase64(jwk.x))); |
|
|
|
var y = toHex(base64ToUint8(urlBase64ToBase64(jwk.y))); |
|
|
|
var ssh = Hex.toUint8( |
|
|
|
('00 00 00 13 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70' |
|
|
|
+ a + '00 00 00 08 6e 69 73 74 70' + a + '00 00 00' + b |
|
|
|
+ '04' + x + y).replace(/\s+/g, '').toLowerCase() |
|
|
|
); |
|
|
|
|
|
|
|
return typ + ' ' + toBase64(ssh) + ' ' + comment; |
|
|
|
}; |
|
|
|
|
|
|
|
//
|
|
|
|
// A dumbed-down, minimal ASN.1 packer
|
|
|
|