Browse Source

v0.8.0: JWK-to-PEM for PKCS#1 and SSH

tags/v0.8.0
AJ ONeal 1 year ago
parent
commit
607e352b17
7 changed files with 74 additions and 8 deletions
  1. 5
    4
      README.md
  2. 0
    0
      fixtures/privkey-rsa-2048.jwk.json
  3. 5
    0
      fixtures/pub-rsa-2048.jwk.json
  4. 19
    1
      lib/encoding.js
  5. 1
    1
      lib/rasha.js
  6. 43
    1
      lib/ssh.js
  7. 1
    1
      package.json

+ 5
- 4
README.md View File

@@ -17,7 +17,8 @@ It is considered to be complete, but if you find a bug please open an issue. -->
17 17
 
18 18
 ## PEM-to-JWK
19 19
 
20
-* [x] PKCS#1 (traditional), PKCS#8, SPKI/PKIX
20
+* [x] PKCS#1 (traditional)
21
+* [x] PKCS#8, SPKI/PKIX
21 22
 * [x] 2048-bit, 4096-bit (and ostensibily all others)
22 23
 * [x] SSH (RFC4716), (RFC 4716/SSH2)
23 24
 
@@ -45,16 +46,16 @@ Rasha.import({ pem: pem }).then(function (jwk) {
45 46
 }
46 47
 ```
47 48
 
48
-<!--
49 49
 ## JWK-to-PEM
50 50
 
51
-* [x] PKCS#1 (traditional), PKCS#8, SPKI/PKIX
51
+* [x] PKCS#1 (traditional)
52
+* [ ] PKCS#8, SPKI/PKIX
52 53
 * [x] 2048-bit, 4096-bit (and ostensibily all others)
53 54
 * [x] SSH (RFC4716), (RFC 4716/SSH2)
54 55
 
55 56
 ```js
56 57
 var Rasha = require('rasha');
57
-var jwk = require('rasha/fixtures/privkey-rsa-2038.jwk.json');
58
+var jwk = require('rasha/fixtures/privkey-rsa-2048.jwk.json');
58 59
 
59 60
 Rasha.export({ jwk: jwk }).then(function (pem) {
60 61
   // PEM in PKCS1 (traditional) format

fixtures/privkey-rsa-2048.jwt.json → fixtures/privkey-rsa-2048.jwk.json View File


+ 5
- 0
fixtures/pub-rsa-2048.jwk.json View File

@@ -0,0 +1,5 @@
1
+{
2
+  "kty": "RSA",
3
+  "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
4
+  "e": "AQAB"
5
+}

+ 19
- 1
lib/encoding.js View File

@@ -15,6 +15,10 @@ Enc.bufToHex = function toHex(u8) {
15 15
   return hex.join('').toLowerCase();
16 16
 };
17 17
 
18
+Enc.hexToBase64 = function (hex) {
19
+  return Buffer.from(hex, 'hex').toString('base64');
20
+};
21
+
18 22
 Enc.hexToBuf = function (hex) {
19 23
   return Buffer.from(hex, 'hex');
20 24
 };
@@ -29,7 +33,7 @@ Enc.numToHex = function numToHex(d) {
29 33
 
30 34
 Enc.base64ToHex = function base64ToHex(b64) {
31 35
   return Enc.bufToHex(Enc.base64ToBuf(b64));
32
-}
36
+};
33 37
 
34 38
 Enc.bufToBase64 = function toHex(u8) {
35 39
   // we want to maintain api compatability with browser APIs,
@@ -37,11 +41,25 @@ Enc.bufToBase64 = function toHex(u8) {
37 41
   return Buffer.from(u8).toString('base64');
38 42
 };
39 43
 
44
+/*
45
+Enc.bufToUint8 = function bufToUint8(buf) {
46
+  return new Uint8Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));
47
+};
48
+*/
49
+
40 50
 Enc.bufToUrlBase64 = function toHex(u8) {
41 51
   return Enc.bufToBase64(u8)
42 52
     .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
43 53
 };
44 54
 
55
+Enc.strToHex = function strToHex(str) {
56
+  return Buffer.from(str).toString('hex');
57
+};
58
+
59
+Enc.strToBuf = function strToBuf(str) {
60
+  return Buffer.from(str);
61
+};
62
+
45 63
 /*
46 64
 Enc.strToBin = function strToBin(str) {
47 65
   var escstr = encodeURIComponent(str);

+ 1
- 1
lib/rasha.js View File

@@ -100,7 +100,7 @@ RSA.pack = function (opts) {
100 100
     } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
101 101
       return PEM.packBlock({ type: "PUBLIC KEY", bytes: x509.packSpki(jwk) });
102 102
     } else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
103
-      return SSH.packSsh(jwk);
103
+      return SSH.pack({ jwk: jwk, comment: opts.comment });
104 104
     } else {
105 105
       throw new Error("Sanity Error: reached unreachable code block with format: " + format);
106 106
     }

+ 43
- 1
lib/ssh.js View File

@@ -17,6 +17,7 @@ SSH.parse = function (pem, jwk) {
17 17
   var offset = (buf.byteOffset || 0);
18 18
   // using dataview to be browser-compatible (I do want _some_ code reuse)
19 19
   var dv = new DataView(buf.buffer.slice(offset, offset + buf.byteLength));
20
+  var el;
20 21
 
21 22
   if (SSH.RSA !== Enc.bufToHex(buf.slice(0, SSH.RSA.length/2))) {
22 23
     throw new Error("does not lead with ssh header");
@@ -27,7 +28,12 @@ SSH.parse = function (pem, jwk) {
27 28
     if (i > 3) { throw new Error("15+ elements, probably not a public ssh key"); }
28 29
     len = dv.getUint32(index, false);
29 30
     index += 4;
30
-    els.push(buf.slice(index, index + len));
31
+    el = buf.slice(index, index + len);
32
+    // remove BigUInt '00' prefix
33
+    if (0x00 === el[0]) {
34
+      el = el.slice(1);
35
+    }
36
+    els.push(el);
31 37
     index += len;
32 38
   }
33 39
 
@@ -36,3 +42,39 @@ SSH.parse = function (pem, jwk) {
36 42
 
37 43
   return jwk;
38 44
 };
45
+
46
+SSH.pack = function (opts) {
47
+  var jwk = opts.jwk;
48
+  var header = 'ssh-rsa';
49
+  var comment = opts.comment || 'rsa@localhost';
50
+  var e = SSH._padHexInt(Enc.base64ToHex(jwk.e));
51
+  var n = SSH._padHexInt(Enc.base64ToHex(jwk.n));
52
+  var hex = [
53
+    SSH._numToUint32Hex(header.length)
54
+  , Enc.strToHex(header)
55
+  , SSH._numToUint32Hex(e.length/2)
56
+  , e
57
+  , SSH._numToUint32Hex(n.length/2)
58
+  , n
59
+  ].join('');
60
+  return [ header, Enc.hexToBase64(hex), comment ].join(' ');
61
+};
62
+
63
+SSH._numToUint32Hex = function (num) {
64
+  var hex = num.toString(16);
65
+  while (hex.length < 8) {
66
+    hex = '0' + hex;
67
+  }
68
+  return hex;
69
+};
70
+
71
+SSH._padHexInt = function (hex) {
72
+  // BigInt is negative if the high order bit 0x80 is set,
73
+  // so ASN1, SSH, and many other formats pad with '0x00'
74
+  // to signifiy a positive number.
75
+  var i = parseInt(hex.slice(0, 2), 16);
76
+  if (0x80 & i) {
77
+    return '00' + hex;
78
+  }
79
+  return hex;
80
+};

+ 1
- 1
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rasha",
3
-  "version": "0.7.1",
3
+  "version": "0.8.0",
4 4
   "description": "PEM-to-JWK and JWK-to-PEM for RSA keys in a lightweight, zero-dependency library focused on perfect universal compatibility.",
5 5
   "homepage": "https://git.coolaj86.com/coolaj86/rasha.js",
6 6
   "main": "index.js",

Loading…
Cancel
Save