Skip to content

Commit

Permalink
Update: export EC keys as PEM
Browse files Browse the repository at this point in the history
  • Loading branch information
linuxwolf committed Dec 11, 2015
1 parent d190cf2 commit 71d382e
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 14 deletions.
91 changes: 77 additions & 14 deletions lib/jwk/eckey.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ var WRAP_ALGS = [
"ECDH-ES+A256KW"
];

var EC_OID = "1.2.840.10045.2.1";
function oidToCurveName(oid) {
switch (oid) {
case "1.2.840.10045.3.1.7":
return "P-256";
case "1.3.132.0.34":
return "P-384";
case "1.3.132.0.35":
return "P-521";
default:
return null;
}
}
function curveNameToOid(crv) {
switch (crv) {
case "P-256":
return "1.2.840.10045.3.1.7";
case "P-384":
return "1.3.132.0.34";
case "P-521":
return "1.3.132.0.35";
default:
return null;
}
}

var JWKEcCfg = {
publicKey: function(props) {
var fields = JWK.helpers.COMMON_PROPS.concat([
Expand Down Expand Up @@ -113,12 +139,62 @@ var JWKEcCfg = {
},
verifyKey: function(alg, keys) {
return keys.public;
},

convertToPEM: function(key, isPrivate) {
// curveName to OID
var oid = key.crv;
oid = curveNameToOid(oid);
oid = forge.asn1.oidToDer(oid);
// key as bytes
var type,
pub,
asn1;
if (isPrivate) {
type = "EC PRIVATE KEY";
pub = Buffer.concat([
new Buffer([0x00, 0x04]),
key.x,
key.y
]).toString("binary");
key = key.d.toString("binary");
asn1 = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.INTEGER, false, "\u0001"),
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OCTETSTRING, false, key),
forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 0, true, [
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, oid.bytes())
]),
forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 1, true, [
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.BITSTRING, false, pub)
])
]);
} else {
type = "PUBLIC KEY";
key = Buffer.concat([
new Buffer([0x00, 0x04]),
key.x,
key.y
]).toString("binary");
asn1 = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, forge.asn1.oidToDer(EC_OID).bytes()),
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, oid.bytes())
]),
forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.BITSTRING, false, key)
]);
}
asn1 = forge.asn1.toDer(asn1).bytes();
var pem = forge.pem.encode({
type: type,
body: asn1
});
return pem;
}
};

// Inspired by digitalbaazar/node-forge/js/rsa.js
var validators = {
oid: "1.2.840.10045.2.1",
oid: EC_OID,
privateKey: {
// ECPrivateKey
name: "ECPrivateKey",
Expand Down Expand Up @@ -215,19 +291,6 @@ var validators = {
}
};

function oidToCurveName(oid) {
switch (oid) {
case "1.2.840.10045.3.1.7":
return "P-256";
case "1.3.132.0.34":
return "P-384";
case "1.3.132.0.35":
return "P-521";
default:
return null;
}
}

var JWKEcFactory = {
kty: "EC",
validators: validators,
Expand Down
12 changes: 12 additions & 0 deletions test/jwk/eckey-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,18 @@ describe("jwk/EC", function() {
algs = JWK.EC.config.algorithms(keys, "verify");
assert.deepEqual(algs, []);
});
it("exports PEM for public key", function() {
var pem = JWK.EC.config.convertToPEM(keyPair.public, false);
assert.isString(pem);
assert.match(pem, /^-----BEGIN PUBLIC KEY-----\r\n/);
assert.match(pem, /\r\n-----END PUBLIC KEY-----\r\n$/);
});
it("exports PEM for private key", function() {
var pem = JWK.EC.config.convertToPEM(keyPair.private, true);
assert.isString(pem);
assert.match(pem, /^-----BEGIN EC PRIVATE KEY-----\r\n/);
assert.match(pem, /\r\n-----END EC PRIVATE KEY-----\r\n$/);
});
});
describe("keystore integration", function() {
it("generates a 'EC' JWK", function() {
Expand Down
10 changes: 10 additions & 0 deletions test/jwk/keystore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ describe("jwk/keystore", function() {
var promise = JWK.store.KeyStore.asKey(pem.rawPrivate, "pem");
promise = promise.then(function(jwk) {
assert.ok(JWK.store.KeyStore.asKey(jwk));
return jwk.toPEM(true);
});
promise = promise.then(function(pem) {
assert.match(pem, /^-----BEGIN EC PRIVATE KEY-----\r\n/);
assert.match(pem, /\r\n-----END EC PRIVATE KEY-----\r\n$/);
});

return promise;
Expand All @@ -594,6 +599,11 @@ describe("jwk/keystore", function() {
var promise = JWK.store.KeyStore.asKey(pem.spki, "pem");
promise = promise.then(function(jwk) {
assert.ok(JWK.store.KeyStore.asKey(jwk));
return jwk.toPEM(false);
});
promise = promise.then(function(pem) {
assert.match(pem, /^-----BEGIN PUBLIC KEY-----\r\n/);
assert.match(pem, /\r\n-----END PUBLIC KEY-----\r\n$/);
});

return promise;
Expand Down

0 comments on commit 71d382e

Please sign in to comment.