Skip to content

Commit cab7fc1

Browse files
committed
Update: import a (RSA and EC) key from ASN.1 (PEM or DER)
1 parent d1267b2 commit cab7fc1

File tree

6 files changed

+848
-21
lines changed

6 files changed

+848
-21
lines changed

README.md

+41-2
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,32 @@ To import an existing Key (as a JSON object or Key instance):
168168
// input is either a:
169169
// * jose.JWK.Key to copy from; or
170170
// * JSON object representing a JWK; or
171-
// * String serialization of a JWK
172171
keystore.add(input).
173172
then(function(result) {
174173
// {result} is a jose.JWK.Key
175174
key = result;
176175
});
177176
```
178177

178+
To import and existing Key from a PEM or DER:
179+
180+
```
181+
// input is either a:
182+
// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
183+
// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
184+
// form is either a:
185+
// * "json" for a JSON stringified JWK
186+
// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key
187+
// * "spki" for a DER encoded SPKI public key
188+
// * "pkix" for a DER encoded PKIX X.509 certificate
189+
// * "x509" for a DER encoded PKIX X.509 certificate
190+
// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX
191+
keystore.add(input, form).
192+
then(function(result) {
193+
// {result} is a jose.JWK.Key
194+
});
195+
```
196+
179197
To generate a new Key:
180198

181199
```
@@ -208,22 +226,43 @@ kestyore.remove(key);
208226

209227
### Importing and Exporting a Single Key ###
210228

211-
To import a single Key (as a JSON Object, or String serialized JSON Object):
229+
To import a single Key:
212230

213231
```
232+
// where input is either a:
233+
// * jose.JWK.Key instance
234+
// * JSON Object representation of a JWK
214235
jose.JWK.asKey(input).
215236
then(function(result) {
216237
// {result} is a jose.JWK.Key
217238
// {result.keystore} is a unique jose.JWK.KeyStore
218239
});
240+
241+
// where input is either a:
242+
// * String serialization of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
243+
// * Buffer of a JSON JWK/(base64-encoded) PEM/(binary-encoded) DER
244+
// form is either a:
245+
// * "json" for a JSON stringified JWK
246+
// * "pkcs8" for a DER encoded (unencrypted!) PKCS8 private key
247+
// * "spki" for a DER encoded SPKI public key
248+
// * "pkix" for a DER encoded PKIX X.509 certificate
249+
// * "x509" for a DER encoded PKIX X.509 certificate
250+
// * "pem" for a PEM encoded of PKCS8 / SPKI / PKIX
251+
jose.JWK.asKey(input, form).
252+
then(function(result) {
253+
// {result} is a jose.JWK.Key
254+
// {result.keystore} is a unique jose.JWK.KeyStore
255+
});
219256
```
220257

221258
To export the public portion of a Key as a JWK:
259+
222260
```
223261
var output = key.toJSON();
224262
```
225263

226264
To export the public **and** private portions of a Key:
265+
227266
```
228267
var output = key.toJSON(true);
229268
```

lib/jwk/eckey.js

+110
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"use strict";
77

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

1112
var JWK = {
@@ -115,6 +116,49 @@ var JWKEcCfg = {
115116
}
116117
};
117118

119+
// Inspired by digitalbaazar/node-forge/js/rsa.js
120+
var validators = {
121+
privateKey: {
122+
// ECPrivateKey
123+
name: "ECPrivateKey",
124+
tagClass: forge.asn1.Class.UNIVERSAL,
125+
type: forge.asn1.Type.SEQUENCE,
126+
constructed: true,
127+
value: [
128+
{
129+
// EC version
130+
name: "ECPrivateKey.version",
131+
tagClass: forge.asn1.Class.UNIVERSAL,
132+
type: forge.asn1.Type.INTEGER,
133+
constructed: false
134+
},
135+
{
136+
// private value (d)
137+
name: "ECPrivateKey.private",
138+
tagClass: forge.asn1.Class.UNIVERSAL,
139+
type: forge.asn1.Type.OCTETSTRING,
140+
constructed: false,
141+
capture: "d"
142+
},
143+
{
144+
// publicKey
145+
name: "ECPrivateKey.publicKey",
146+
tagClass: forge.asn1.Class.CONTEXT_SPECIFIC,
147+
constructed: true,
148+
value: [
149+
{
150+
name: "ECPrivateKey.point",
151+
tagClass: forge.asn1.Class.UNIVERSAL,
152+
type: forge.asn1.Type.BITSTRING,
153+
constructed: false,
154+
capture: "point"
155+
}
156+
]
157+
}
158+
]
159+
}
160+
};
161+
118162
var JWKEcFactory = {
119163
kty: "EC",
120164
prepare: function() {
@@ -129,6 +173,72 @@ var JWKEcFactory = {
129173
"d": keypair.private.d
130174
};
131175
return Promise.resolve(result);
176+
},
177+
import: function(input) {
178+
if ("1.2.840.10045.2.1" !== input.keyOid) {
179+
return null;
180+
}
181+
182+
// coerce key params to OID
183+
var crv;
184+
if (input.keyParams && forge.asn1.Type.OID === input.keyParams.type) {
185+
crv = forge.asn1.derToOid(input.keyParams.value);
186+
// convert OID to common name
187+
switch (crv) {
188+
case "1.2.840.10045.3.1.7":
189+
crv = "P-256";
190+
break;
191+
case "1.3.132.0.34":
192+
crv = "P-384";
193+
break;
194+
case "1.3.132.0.37":
195+
crv = "P-521";
196+
break;
197+
default:
198+
return null;
199+
}
200+
}
201+
202+
var capture = {},
203+
errors = [];
204+
if ("private" === input.type) {
205+
// coerce capture.value to DER *iff* private
206+
if ("string" === typeof input.keyValue) {
207+
input.keyValue = forge.asn1.fromDer(input.keyValue);
208+
} else if (Array.isArray(input.keyValue)) {
209+
input.keyValue = input.keyValue[0];
210+
}
211+
212+
if (!forge.asn1.validate(input.keyValue,
213+
validators.privateKey,
214+
capture,
215+
errors)) {
216+
return null;
217+
}
218+
} else {
219+
capture.point = input.keyValue;
220+
}
221+
222+
// convert factors to Buffers
223+
var output = {
224+
kty: "EC",
225+
crv: crv
226+
};
227+
if (capture.d) {
228+
output.d = new Buffer(capture.d, "binary");
229+
}
230+
if (capture.point) {
231+
var pt = new Buffer(capture.point, "binary");
232+
// only support uncompressed
233+
if (4 !== pt.readUInt16BE(0)) {
234+
return null;
235+
}
236+
pt = pt.slice(2);
237+
var len = pt.length / 2;
238+
output.x = pt.slice(0, len);
239+
output.y = pt.slice(len);
240+
}
241+
return output;
132242
}
133243
};
134244
// public API

0 commit comments

Comments
 (0)