Skip to content

Commit

Permalink
Update: import a (RSA and EC) key from ASN.1 (PEM or DER)
Browse files Browse the repository at this point in the history
  • Loading branch information
linuxwolf committed Oct 9, 2015
1 parent d1267b2 commit cab7fc1
Show file tree
Hide file tree
Showing 6 changed files with 848 additions and 21 deletions.
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,32 @@ To import an existing Key (as a JSON object or Key instance):
// input is either a:
// * jose.JWK.Key to copy from; or
// * JSON object representing a JWK; or
// * String serialization of a JWK
keystore.add(input).
then(function(result) {
// {result} is a jose.JWK.Key
key = result;
});
```

To import and existing Key from a PEM or DER:

```
// input is either a:
// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
// form is either a:
// * "json" for a JSON stringified JWK
// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key
// * "spki" for a DER encoded SPKI public key
// * "pkix" for a DER encoded PKIX X.509 certificate
// * "x509" for a DER encoded PKIX X.509 certificate
// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX
keystore.add(input, form).
then(function(result) {
// {result} is a jose.JWK.Key
});
```

To generate a new Key:

```
Expand Down Expand Up @@ -208,22 +226,43 @@ kestyore.remove(key);

### Importing and Exporting a Single Key ###

To import a single Key (as a JSON Object, or String serialized JSON Object):
To import a single Key:

```
// where input is either a:
// * jose.JWK.Key instance
// * JSON Object representation of a JWK
jose.JWK.asKey(input).
then(function(result) {
// {result} is a jose.JWK.Key
// {result.keystore} is a unique jose.JWK.KeyStore
});
// where input is either a:
// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
// form is either a:
// * "json" for a JSON stringified JWK
// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key
// * "spki" for a DER encoded SPKI public key
// * "pkix" for a DER encoded PKIX X.509 certificate
// * "x509" for a DER encoded PKIX X.509 certificate
// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX
jose.JWK.asKey(input, form).
then(function(result) {
// {result} is a jose.JWK.Key
// {result.keystore} is a unique jose.JWK.KeyStore
});
```

To export the public portion of a Key as a JWK:

```
var output = key.toJSON();
```

To export the public **and** private portions of a Key:

```
var output = key.toJSON(true);
```
Expand Down
110 changes: 110 additions & 0 deletions lib/jwk/eckey.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"use strict";

var ecutil = require("../algorithms/ec-util.js"),
forge = require("../deps/forge"),
depsecc = require("../deps/ecc");

var JWK = {
Expand Down Expand Up @@ -115,6 +116,49 @@ var JWKEcCfg = {
}
};

// Inspired by digitalbaazar/node-forge/js/rsa.js
var validators = {
privateKey: {
// ECPrivateKey
name: "ECPrivateKey",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
constructed: true,
value: [
{
// EC version
name: "ECPrivateKey.version",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.INTEGER,
constructed: false
},
{
// private value (d)
name: "ECPrivateKey.private",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OCTETSTRING,
constructed: false,
capture: "d"
},
{
// publicKey
name: "ECPrivateKey.publicKey",
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
constructed: true,
value: [
{
name: "ECPrivateKey.point",
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BITSTRING,
constructed: false,
capture: "point"
}
]
}
]
}
};

var JWKEcFactory = {
kty: "EC",
prepare: function() {
Expand All @@ -129,6 +173,72 @@ var JWKEcFactory = {
"d": keypair.private.d
};
return Promise.resolve(result);
},
import: function(input) {
if ("1.2.840.10045.2.1" !== input.keyOid) {
return null;
}

// coerce key params to OID
var crv;
if (input.keyParams && forge.asn1.Type.OID === input.keyParams.type) {
crv = forge.asn1.derToOid(input.keyParams.value);
// convert OID to common name
switch (crv) {
case "1.2.840.10045.3.1.7":
crv = "P-256";
break;
case "1.3.132.0.34":
crv = "P-384";
break;
case "1.3.132.0.37":
crv = "P-521";
break;
default:
return null;
}
}

var capture = {},
errors = [];
if ("private" === input.type) {
// coerce capture.value to DER *iff* private
if ("string" === typeof input.keyValue) {
input.keyValue = forge.asn1.fromDer(input.keyValue);
} else if (Array.isArray(input.keyValue)) {
input.keyValue = input.keyValue[0];
}

if (!forge.asn1.validate(input.keyValue,
validators.privateKey,
capture,
errors)) {
return null;
}
} else {
capture.point = input.keyValue;
}

// convert factors to Buffers
var output = {
kty: "EC",
crv: crv
};
if (capture.d) {
output.d = new Buffer(capture.d, "binary");
}
if (capture.point) {
var pt = new Buffer(capture.point, "binary");
// only support uncompressed
if (4 !== pt.readUInt16BE(0)) {
return null;
}
pt = pt.slice(2);
var len = pt.length / 2;
output.x = pt.slice(0, len);
output.y = pt.slice(len);
}
return output;
}
};
// public API
Expand Down
Loading

0 comments on commit cab7fc1

Please sign in to comment.