Browse Source

v0.0.2: WIP, useful as an example

tags/v0.0.2
AJ ONeal 10 months ago
parent
commit
30e617ada1
6 changed files with 629 additions and 2 deletions
  1. 4
    1
      README.md
  2. 16
    0
      convert-to-der.js
  3. 1
    1
      package.json
  4. 23
    0
      pubkey-cli.js
  5. 181
    0
      pubkey.js
  6. 404
    0
      re-sign.js

+ 4
- 1
README.md View File

@@ -1,6 +1,9 @@
1 1
 This is being ported from code from rsa-compat.js, greenlock.html (bacme.js), and others.
2 2
 
3
-Today is 2018-10-10 come back in a week.
3
+This is my project for the weekend. I expect to be finished today (Monday Nov 12th, 2018)
4
+* 2018-10-10 (Saturday) work has begun
5
+* 2018-10-11 (Sunday) W00T! got a CSR generated for RSA with VanillaJS ArrayBuffer
6
+* 2018-10-12 (Monday) Figuring out ECDSA CSRs right now
4 7
 
5 8
 <!--
6 9
 Keypairs&trade; for node.js

+ 16
- 0
convert-to-der.js View File

@@ -0,0 +1,16 @@
1
+'use strict';
2
+
3
+var fs = require('fs');
4
+var path = require('path');
5
+
6
+function convert(name) {
7
+  var ext = path.extname(name);
8
+  var csr = fs.readFileSync(name, 'ascii').split(/\n/).filter(function (line) {
9
+    return !/---/.test(line);
10
+  }).join('');
11
+  console.log(csr);
12
+  var der = Buffer.from(csr, 'base64');
13
+  fs.writeFileSync(name.replace(new RegExp('\\' + ext + '$'), '') + '.der', der);
14
+}
15
+
16
+convert(process.argv[2]);

+ 1
- 1
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "keypairs",
3
-  "version": "0.0.1",
3
+  "version": "0.0.2",
4 4
   "description": "Interchangeably use RSA & ECDSA with PEM and JWK for Signing, Verifying, CSR generation and JOSE. Ugh... that was a mouthful.",
5 5
   "main": "index.js",
6 6
   "scripts": {

+ 23
- 0
pubkey-cli.js View File

@@ -0,0 +1,23 @@
1
+'use strict';
2
+
3
+var pubkey = require('./pubkey.js');
4
+var pubname = process.argv[2];
5
+var fs = require('fs');
6
+var pem = fs.readFileSync(pubname);
7
+
8
+var key = pubkey.parsePem(pem);
9
+if ('RSA' !== key.typ) {
10
+  throw new Error(key.typ + " not supported");
11
+}
12
+if (key.pub) {
13
+  var pubbuf = pubkey.readPubkey(key.der);
14
+} else {
15
+  var pubbuf = pubkey.readPrivkey(key.der);
16
+}
17
+
18
+console.log(pubbuf.byteLength, pubkey.toHex(pubbuf));
19
+var der = pubkey.toRsaPub(pubbuf);
20
+var b64 = pubkey.toBase64(der);
21
+var pem = pubkey.formatAsPublicPem(b64);
22
+console.log('Pub:\n');
23
+console.log(pem);

+ 181
- 0
pubkey.js View File

@@ -0,0 +1,181 @@
1
+(function (exports) {
2
+'use strict';
3
+
4
+// 30 sequence
5
+// 03 bit string
6
+// 05 null
7
+// 06 object id
8
+
9
+//       00                                                         00              00          00
10
+// 30 82 01 22 30 0D 06 09  2A 86 48 86 F7 0D 01 01  01 05 00 03 82 01 0F 00  30 82 01 0A 02 82 01 01
11
+// 00 ... 02 03 01 00 01
12
+// 30 82 02 22 30 0D 06 09  2A 86 48 86 F7 0D 01 01  01 05 00 03 82 02 0F 00  30 82 02 0A 02 82 02 01
13
+// 00 ... 02 03 01 00 01
14
+
15
+function parsePem(pem) {
16
+  var typ;
17
+  var pub;
18
+  var der = fromBase64(pem.toString('ascii').split(/\n/).filter(function (line, i) {
19
+    if (0 === i) {
20
+      if (/ PUBLIC /.test(line)) {
21
+        pub = true;
22
+      } else if (/ PRIVATE /.test(line)) {
23
+        pub = false;
24
+      }
25
+      if (/ RSA /.test(line)) {
26
+        typ = 'RSA';
27
+      } else if (/ EC/.test(line)) {
28
+        typ = 'EC';
29
+      }
30
+    }
31
+    return !/---/.test(line);
32
+  }).join(''));
33
+
34
+  if (!typ) {
35
+    if (pub) {
36
+      // This is the RSA object ID
37
+      if ('06092A864886F70D010101'.toLowerCase() === der.slice(6, 6 + 11).toString('hex')) {
38
+        typ = 'RSA';
39
+      }
40
+    } else {
41
+      // TODO
42
+    }
43
+  }
44
+
45
+  return { typ: typ, pub: pub, der: der };
46
+}
47
+
48
+function toHex(ab) {
49
+  var hex = [];
50
+  var u8 = new Uint8Array(ab);
51
+  var size = u8.byteLength;
52
+  var i;
53
+  var h;
54
+  for (i = 0; i < size; i += 1) {
55
+    h = u8[i].toString(16);
56
+    if (2 === h.length) {
57
+      hex.push(h);
58
+    } else {
59
+      hex.push('0' + h);
60
+    }
61
+  }
62
+  return hex.join('');
63
+}
64
+
65
+function readPubkey(der) {
66
+  var offset = 28 + 5; // header plus size
67
+  var ksBytes = der.slice(30, 32);
68
+  // not sure why it shows 257 instead of 256
69
+  var keysize = new DataView(ksBytes).getUint16(0, false) - 1;
70
+  var pub = der.slice(offset, offset + keysize);
71
+  return pub;
72
+}
73
+
74
+function readPrivkey(der) {
75
+  var offset = 7 + 5; // header plus size
76
+  var ksBytes = der.slice(9, 11);
77
+  // not sure why it shows 257 instead of 256
78
+  var keysize = new DataView(ksBytes).getUint16(0, false) - 1;
79
+  var pub = der.slice(offset, offset + keysize);
80
+  return pub;
81
+}
82
+
83
+
84
+// I used OpenSSL to create RSA keys with sizes 2048 and 4096.
85
+// Then I used https://lapo.it/asn1js/ to see which bits changed.
86
+// And I created a template from the bits that do and don't.
87
+// No ASN.1 and X.509 parsers or generators. Yay!
88
+var rsaAsn1Head = (
89
+    '30 82 xx 22 30 0D 06 09'
90
+  + '2A 86 48 86 F7 0D 01 01'
91
+  + '01 05 00 03 82 xx 0F 00'
92
+  + '30 82 xx 0A 02 82 xx 01'
93
+  + '00').replace(/\s+/g, '');
94
+var rsaAsn1Foot = ('02 03 01 00 01').replace(/\s+/g, '');
95
+function toRsaPub(pub) {
96
+  // 256 // 2048-bit
97
+  var len = '0' + (pub.byteLength / 256);
98
+  var head = rsaAsn1Head.replace(/xx/g, len);
99
+  var headSize = (rsaAsn1Head.length / 2);
100
+  var foot = rsaAsn1Foot;
101
+  var footSize = (foot.length / 2);
102
+
103
+  var size = headSize + pub.byteLength + footSize;
104
+  var der = new Uint8Array(new ArrayBuffer(size));
105
+
106
+  var i, j;
107
+  for (i = 0, j = 0; i < headSize; i += 1) {
108
+    der[i] = parseInt(head.slice(j,j+2), 16);
109
+    j += 2;
110
+  }
111
+
112
+  pub = new Uint8Array(pub);
113
+  for (i = 0; i < pub.byteLength; i += 1) {
114
+    der[headSize + i] = pub[i];
115
+  }
116
+
117
+  for (i = 0, j = 0; i < footSize; i += 1) {
118
+    der[headSize + pub.byteLength + i] = parseInt(foot.slice(j,j+2), 16);
119
+    j += 2;
120
+  }
121
+
122
+  return der.buffer;
123
+}
124
+
125
+function formatAsPem(str, privacy, pemName) {
126
+  var pemstr = (pemName ? pemName + ' ' : '');
127
+  var privstr = (privacy ? privacy + ' ' : '');
128
+  var finalString = '-----BEGIN ' + pemstr + privstr + 'KEY-----\n';
129
+
130
+  while (str.length > 0) {
131
+      finalString += str.substring(0, 64) + '\n';
132
+      str = str.substring(64);
133
+  }
134
+
135
+  finalString = finalString + '-----END ' + pemstr + privstr + 'KEY-----';
136
+
137
+  return finalString;
138
+}
139
+
140
+function formatAsPublicPem(str) {
141
+  return formatAsPem(str, 'PUBLIC', '');
142
+}
143
+
144
+function toBase64(der) {
145
+  if ('undefined' === typeof btoa) {
146
+    return Buffer.from(der).toString('base64');
147
+  }
148
+	var chs = [];
149
+	der = new Uint8Array(der);
150
+	der.forEach(function (b) {
151
+		chs.push(String.fromCharCode(b));
152
+	});
153
+  return btoa(chs.join(''));
154
+}
155
+
156
+function fromBase64(b64) {
157
+  var buf;
158
+  var ab;
159
+  if ('undefined' === typeof atob) {
160
+    buf = Buffer.from(b64, 'base64');
161
+    return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
162
+  }
163
+  buf = atob(b64);
164
+  ab = new ArrayBuffer(buf.length);
165
+  ab = new Uint8Array(ab);
166
+  buf.split('').forEach(function (ch, i) {
167
+    ab[i] = ch.charCodeAt(0);
168
+  });
169
+  return ab.buffer;
170
+}
171
+
172
+exports.parsePem = parsePem;
173
+exports.toBase64 = toBase64;
174
+exports.toRsaPub = toRsaPub;
175
+exports.formatAsPublicPem = formatAsPublicPem;
176
+exports.formatAsPem = formatAsPem;
177
+exports.readPubkey = readPubkey;
178
+exports.readPrivkey = readPrivkey;
179
+exports.toHex = toHex;
180
+
181
+}('undefined' !== typeof module ? module.exports: window));

+ 404
- 0
re-sign.js View File

@@ -0,0 +1,404 @@
1
+'use strict';
2
+
3
+var crypto = require('crypto');
4
+var fs = require('fs');
5
+var pubkey = require('./pubkey.js');
6
+
7
+var keyname = process.argv[2];
8
+var dername = process.argv[3];
9
+
10
+var keypem = fs.readFileSync(keyname);
11
+var csrFull = fs.readFileSync(dername);
12
+var csrFull = csrFull.buffer.slice(csrFull.byteOffset, csrFull.byteOffset + csrFull.byteLength);
13
+
14
+// these are static ASN.1 segments
15
+// The head specifies that there will be 3 segments and a content length
16
+// (those segments will be content, signature header, and signature)
17
+var csrHead = '30 82 {0seq0len}'.replace(/\s+/g, '');
18
+// The tail specifies the RSA256 signature header (and is followed by the signature
19
+var csrRsaFoot =
20
+  ( '30 0D 06 09 2A 86 48 86 F7 0D 01 01 0B'
21
+  + '05 00'
22
+  + '03 82 01 01 00'
23
+  ).replace(/\s+/g, '');
24
+var csrDomains = '82 {dlen} {domain.tld}';  // 2+n bytes (type 82?)
25
+/*
26
+var csrRsaContent =
27
+  '30 82 {1.1.0seqlen}'                     // 7+n    4 bytes, sequence
28
+  + '02 01 00'                              // 3      3 bytes, int 0
29
+
30
+  + '30 {3.2.0seqlen}'                      // 13+n   2 bytes, sequence
31
+    + '31 {4.3.0setlen}'                    // 11+n   2 bytes, set
32
+      + '30 {5.4.0seqlen}'                  // 9+n    2 bytes, sequence
33
+      + '06 03 55 04 03'                    // 7+n    5 bytes, object id (commonName)
34
+      + '0C {dlen} {domain.tld}'            // 2+n    2+n bytes, utf8string
35
+
36
+  + '30 82 {8.2.2seqlen}'                   // 19     4 bytes, sequence
37
+    + '30 0D'                               // 15     2 bytes, sequence
38
+      + '06 09 2A 86 48 86 F7 0D 01 01 01'  // 13     11 bytes, rsaEncryption (PKCS #1)
39
+      + '05 00'                             // 2      2 bytes, null (why?)
40
+    + '03 82 {12.3.0bslen} 00'              // +18+m  5 bytes, bit string [01 0F]
41
+      + '30 82 {13.4.0seqlen}'              // +13+m  4 bytes, sequence
42
+      + '02 82 {klen} 00 {key}'             // +9+m   4+1+n bytes, int (RSA Pub Key)
43
+      + '02 03 {mod}'                       // +5     5 bytes, key and modules [01 00 01]
44
+
45
+  + 'A0 {16.2.3ellen}'                      // 30+n   2 bytes, ?? [4B]
46
+    + '30 {17.3.9seqlen}'                   // 28+n   2 bytes, sequence
47
+      + '06 09 2A 86 48 86 F7 0D 01 09 0E'  // 26+n   11 bytes, object id (extensionRequest (PKCS #9 via CRMF))
48
+        + '31 {19.5.0setlen}'               // 15+n   2 bytes, set
49
+          + '30 {20.6.0seqlen}'             // 13+n   2 bytes, sequence
50
+            + '30 {21.7.0seqlen}'           // 11+n   2 bytes, sequence
51
+              + '06 03 55 1D 11'            // 9+n    5 bytes, object id (subjectAltName (X.509 extension))
52
+              + '04 {23.8.0octlen}'         // 4+n    2 bytes, octet string
53
+                + '30 {24.9.0seqlen}'       // 2+n    2 bytes, sequence
54
+                  + '{altnames}'            // n      n bytes
55
+;
56
+*/
57
+
58
+function privateToPub(pem) {
59
+  var pubbuf;
60
+  var key = pubkey.parsePem(pem);
61
+  if ('RSA' !== key.typ) {
62
+    throw new Error(key.typ + " not supported");
63
+  }
64
+  if (key.pub) {
65
+    pubbuf = pubkey.readPubkey(key.der);
66
+  } else {
67
+    pubbuf = pubkey.readPrivkey(key.der);
68
+  }
69
+
70
+  //console.log(pubbuf.byteLength, pubkey.toHex(pubbuf));
71
+  var der = pubkey.toRsaPub(pubbuf);
72
+  var b64 = pubkey.toBase64(der);
73
+  return pubkey.formatAsPublicPem(b64);
74
+}
75
+
76
+function strToHex(str) {
77
+  return str.split('').map(function (ch) {
78
+    var h = ch.charCodeAt(0).toString(16);
79
+    if (2 === h.length) {
80
+      return h;
81
+    }
82
+    return '0' + h;
83
+  }).join('');
84
+}
85
+
86
+function pubToPem(pubbuf) {
87
+  var der = pubkey.toRsaPub(pubbuf);
88
+  var b64 = pubkey.toBase64(der);
89
+  return pubkey.formatAsPublicPem(b64);
90
+}
91
+
92
+var sigend = (csrFull.byteLength - (2048 / 8));
93
+var sig = csrFull.slice(sigend);
94
+
95
+console.log();
96
+console.log();
97
+console.log('csr (' + csrFull.byteLength + ')');
98
+console.log(pubkey.toHex(csrFull));
99
+console.log();
100
+
101
+// First 4 bytes define Segment, segment length, and content length
102
+console.log(sigend, csrRsaFoot, csrRsaFoot.length/2);
103
+var csrbody = csrFull.slice(4, sigend - (csrRsaFoot.length/2));
104
+console.log('csr body (' + csrbody.byteLength + ')');
105
+console.log(pubkey.toHex(csrbody));
106
+console.log();
107
+
108
+var csrpub = csrFull.slice(63 + 5, 63 + 5 + 256);
109
+console.log('csr pub (' + csrpub.byteLength + ')');
110
+console.log(pubkey.toHex(csrpub));
111
+console.log();
112
+
113
+console.log('sig (' + sig.byteLength + ')');
114
+console.log(pubkey.toHex(sig));
115
+console.log();
116
+
117
+var csrpem = pubToPem(csrpub);
118
+console.log(csrpem);
119
+console.log();
120
+
121
+var prvpem = privateToPub(keypem);
122
+console.log(prvpem);
123
+console.log();
124
+
125
+if (csrpem === prvpem) {
126
+  console.log("Public Keys Match");
127
+} else {
128
+  throw new Error("public key read from keyfile doesn't match public key read from CSR");
129
+}
130
+
131
+function h(d) {
132
+  d = d.toString(16);
133
+  if (d.length % 2) {
134
+    return '0' + d;
135
+  }
136
+  return d;
137
+}
138
+
139
+function fromHex(hex) {
140
+  if ('undefined' !== typeof Buffer) {
141
+    return Buffer.from(hex, 'hex');
142
+  }
143
+  var ab = new ArrayBuffer(hex.length/2);
144
+  var i;
145
+  var j;
146
+  ab = new Uint8Array(ab);
147
+  for (i = 0, j = 0; i < (hex.length/2); i += 1) {
148
+    ab[i] = parseInt(hex.slice(j, j+1), 16);
149
+    j += 2;
150
+  }
151
+  return ab.buffer;
152
+}
153
+
154
+function createCsrBodyRsa(domains, csrpub) {
155
+  var altnames = domains.map(function (d) {
156
+    return csrDomains.replace(/{dlen}/, h(d.length)).replace(/{domain\.tld}/, strToHex(d));
157
+  }).join('').replace(/\s+/g, '');
158
+  var publen = csrpub.byteLength;
159
+  var sublen = domains[0].length;
160
+  var sanlen = (altnames.length/2);
161
+
162
+  var body = [ '30 82 {1.1.0seqlen}'                                  // 4 bytes, sequence
163
+    .replace(/{[^}]+}/, h(
164
+        3
165
+      + 13 + sublen
166
+      + 38 + publen
167
+      + 30 + sanlen
168
+    ))
169
+
170
+      // #0 Total 3
171
+    , '02 01 00'                                                      // 3 bytes, int 0
172
+
173
+      // #1 Total 2+11+n
174
+    , '30 {3.2.0seqlen}'                                              // 2 bytes, sequence
175
+      .replace(/{[^}]+}/, h(2+2+5+2+sublen))
176
+      , '31 {4.3.0setlen}'                                            // 2 bytes, set
177
+        .replace(/{[^}]+}/, h(2+5+2+sublen))
178
+        , '30 {5.4.0seqlen}'                                          // 2 bytes, sequence
179
+          .replace(/{[^}]+}/, h(5+2+sublen))
180
+        , '06 03 55 04 03'                                            // 5 bytes, object id (commonName)
181
+        , '0C {dlen} {domain.tld}'                                    // 2+n bytes, utf8string
182
+          .replace(/{dlen}/, h(sublen))
183
+          .replace(/{domain\.tld}/, strToHex(domains[0]))
184
+
185
+      // #2 Total 4+28+n+1+5
186
+    , '30 82 {8.2.2seqlen}'                                           // 4 bytes, sequence
187
+      .replace(/{[^}]+}/, h(2+11+2+4+1+4+4+publen+1+5))
188
+      , '30 0D'                                                       // 2 bytes, sequence
189
+        , '06 09 2A 86 48 86 F7 0D 01 01 01'                          // 11 bytes, rsaEncryption (PKCS #1)
190
+        , '05 00'                                                     // 2 bytes, null (why?)
191
+      , '03 82 {12.3.0bslen} 00'                                      // 4+1 bytes, bit string [01 0F]
192
+        .replace(/{[^}]+}/, h(1+4+4+publen+1+5))
193
+        , '30 82 {13.4.0seqlen}'                                      // 4 bytes, sequence
194
+          .replace(/{[^}]+}/, h(4+publen+1+5))
195
+        , '02 82 {klen} 00 {key}'                                     // 4+n bytes, int (RSA Pub Key)
196
+          .replace(/{klen}/, h(publen+1))
197
+          .replace(/{key}/, pubkey.toHex(csrpub))
198
+        , '02 03 {mod}'                                               // 5 bytes, key and modules [01 00 01]
199
+          .replace(/{mod}/, '01 00 01')
200
+
201
+      // #3 Total 2+28+n
202
+    , 'A0 {16.2.3ellen}'                                              // 2 bytes, ?? [4B]
203
+      .replace(/{[^}]+}/, h(2+11+2+2+2+5+2+2+sanlen))
204
+      , '30 {17.3.9seqlen}'                                           // 2 bytes, sequence
205
+        .replace(/{[^}]+}/, h(11+2+2+2+5+2+2+sanlen))
206
+        , '06 09 2A 86 48 86 F7 0D 01 09 0E'                          // 11 bytes, object id (extensionRequest (PKCS #9 via CRMF))
207
+          , '31 {19.5.0setlen}'                                       // 2 bytes, set
208
+            .replace(/{[^}]+}/, h(2+2+5+2+2+sanlen))
209
+            , '30 {20.6.0seqlen}'                                     // 2 bytes, sequence
210
+              .replace(/{[^}]+}/, h(2+5+2+2+sanlen))
211
+              , '30 {21.7.0seqlen}'                                   // 2 bytes, sequence
212
+                .replace(/{[^}]+}/, h(5+2+2+sanlen))
213
+                , '06 03 55 1D 11'                                    // 5 bytes, object id (subjectAltName (X.509 extension))
214
+                , '04 {23.8.0octlen}'                                 // 2 bytes, octet string
215
+                  .replace(/{[^}]+}/, h(2+sanlen))
216
+                  , '30 {24.9.0seqlen}'                               // 2 bytes, sequence
217
+                    .replace(/{[^}]+}/, h(sanlen))
218
+                    , '{altnames}'                                    // n (elements of sequence)
219
+                      .replace(/{altnames}/, altnames)
220
+  ];
221
+  body = body.join('').replace(/\s+/g, '');
222
+  return fromHex(body);
223
+}
224
+
225
+function createCsrBodyEc(domains, csrpub) {
226
+  var altnames = domains.map(function (d) {
227
+    return csrDomains.replace(/{dlen}/, h(d.length)).replace(/{domain\.tld}/, strToHex(d));
228
+  }).join('').replace(/\s+/g, '');
229
+  var publen = csrpub.byteLength;
230
+  var sublen = domains[0].length;
231
+  var sanlen = (altnames.length/2);
232
+
233
+  var body = [ '30 82 {1.1.0seqlen}'                                  // 4 bytes, sequence
234
+    .replace(/{[^}]+}/, h(
235
+        3
236
+      + 13 + sublen
237
+      + 38 + publen
238
+      + 30 + sanlen
239
+    ))
240
+
241
+      // #0 Total 3
242
+    , '02 01 00'                                                      // 3 bytes, int 0
243
+
244
+      // #1 Total 2+11+n
245
+    , '30 {3.2.0seqlen}'                                              // 2 bytes, sequence
246
+      .replace(/{[^}]+}/, h(2+2+5+2+sublen))
247
+      , '31 {4.3.0setlen}'                                            // 2 bytes, set
248
+        .replace(/{[^}]+}/, h(2+5+2+sublen))
249
+        , '30 {5.4.0seqlen}'                                          // 2 bytes, sequence
250
+          .replace(/{[^}]+}/, h(5+2+sublen))
251
+        , '06 03 55 04 03'                                            // 5 bytes, object id (commonName)
252
+        , '0C {dlen} {domain.tld}'                                    // 2+n bytes, utf8string
253
+          .replace(/{dlen}/, h(sublen))
254
+          .replace(/{domain\.tld}/, strToHex(domains[0]))
255
+
256
+      // #2 Total 4+28+n+1+5
257
+    , '30 82 {8.2.2seqlen}'                                           // 4 bytes, sequence
258
+      .replace(/{[^}]+}/, h(2+11+2+4+1+4+4+publen+1+5))
259
+      , '30 0D'                                                       // 2 bytes, sequence
260
+        , '06 09 2A 86 48 86 F7 0D 01 01 01'                          // 11 bytes, rsaEncryption (PKCS #1)
261
+        , '05 00'                                                     // 2 bytes, null (why?)
262
+      , '03 82 {12.3.0bslen} 00'                                      // 4+1 bytes, bit string [01 0F]
263
+        .replace(/{[^}]+}/, h(1+4+4+publen+1+5))
264
+        , '30 82 {13.4.0seqlen}'                                      // 4 bytes, sequence
265
+          .replace(/{[^}]+}/, h(4+publen+1+5))
266
+        , '02 82 {klen} 00 {key}'                                     // 4+n bytes, int (RSA Pub Key)
267
+          .replace(/{klen}/, h(publen+1))
268
+          .replace(/{key}/, pubkey.toHex(csrpub))
269
+        , '02 03 {mod}'                                               // 5 bytes, key and modules [01 00 01]
270
+          .replace(/{mod}/, '01 00 01')
271
+
272
+      // #3 Total 2+28+n
273
+    , 'A0 {16.2.3ellen}'                                              // 2 bytes, ?? [4B]
274
+      .replace(/{[^}]+}/, h(2+11+2+2+2+5+2+2+sanlen))
275
+      , '30 {17.3.9seqlen}'                                           // 2 bytes, sequence
276
+        .replace(/{[^}]+}/, h(11+2+2+2+5+2+2+sanlen))
277
+        , '06 09 2A 86 48 86 F7 0D 01 09 0E'                          // 11 bytes, object id (extensionRequest (PKCS #9 via CRMF))
278
+          , '31 {19.5.0setlen}'                                       // 2 bytes, set
279
+            .replace(/{[^}]+}/, h(2+2+5+2+2+sanlen))
280
+            , '30 {20.6.0seqlen}'                                     // 2 bytes, sequence
281
+              .replace(/{[^}]+}/, h(2+5+2+2+sanlen))
282
+              , '30 {21.7.0seqlen}'                                   // 2 bytes, sequence
283
+                .replace(/{[^}]+}/, h(5+2+2+sanlen))
284
+                , '06 03 55 1D 11'                                    // 5 bytes, object id (subjectAltName (X.509 extension))
285
+                , '04 {23.8.0octlen}'                                 // 2 bytes, octet string
286
+                  .replace(/{[^}]+}/, h(2+sanlen))
287
+                  , '30 {24.9.0seqlen}'                               // 2 bytes, sequence
288
+                    .replace(/{[^}]+}/, h(sanlen))
289
+                    , '{altnames}'                                    // n (elements of sequence)
290
+                      .replace(/{altnames}/, altnames)
291
+  ];
292
+  body = body.join('').replace(/\s+/g, '');
293
+  return fromHex(body);
294
+}
295
+
296
+function createCsr(domains, keypem) {
297
+  // TODO get pub from priv
298
+  var body = createCsrBodyRsa(domains, csrpub);
299
+  var sign = crypto.createSign('SHA256');
300
+  sign.write(new Uint8Array(body));
301
+  sign.end();
302
+  var sig = sign.sign(keypem);
303
+  var len = body.byteLength + (csrRsaFoot.length/2) + sig.byteLength;
304
+  console.log('headlen', h(len));
305
+  var head = csrHead.replace(/{[^}]+}/, h(len));
306
+  var ab = new Uint8Array(new ArrayBuffer(4 + len));
307
+  var i = 0;
308
+  fromHex(head).forEach(function (b) {
309
+    ab[i] = b;
310
+    i += 1;
311
+  });
312
+  body.forEach(function (b) {
313
+    ab[i] = b;
314
+    i += 1;
315
+  });
316
+  fromHex(csrRsaFoot).forEach(function (b) {
317
+    ab[i] = b;
318
+    i += 1;
319
+  });
320
+  new Uint8Array(sig).forEach(function (b) {
321
+    ab[i] = b;
322
+    i += 1;
323
+  });
324
+
325
+  var pem = pubkey.formatAsPublicPem(pubkey.toBase64(ab));
326
+
327
+  return pem;
328
+}
329
+
330
+//var pem = createCsr([ 'example.com', 'www.example.com', 'api.example.com' ], keypem);
331
+var pem = createCsr([ 'whatever.net', 'api.whatever.net' ], keypem);
332
+console.log('pem:');
333
+console.log(pem);
334
+return;
335
+
336
+function signagain(csrbody) {
337
+  var longEnough = csrbody.byteLength > 256;
338
+
339
+  if (!longEnough) { return false; }
340
+
341
+  var domains = [ 'example.com', 'www.example.com', 'api.example.com' ];
342
+  var body = createCsrBodyRsa(domains);
343
+
344
+  var sign = crypto.createSign('SHA256');
345
+  sign.write(new Uint8Array(body));
346
+  sign.end();
347
+  var sig2 = sign.sign(keypem);
348
+  var hexsig = pubkey.toHex(sig);
349
+  var hexsig2 = pubkey.toHex(sig2);
350
+
351
+  console.log('hexsig:');
352
+  console.log(hexsig);
353
+  if (hexsig2 !== hexsig) {
354
+    throw new Error("sigs didn't match");
355
+  }
356
+
357
+  console.log('Winner winner!', csrbody.byteLength);
358
+  console.log(pubkey.toHex(csrbody));
359
+  console.log();
360
+  console.log('Test:', body.byteLength);
361
+  console.log(pubkey.toHex(body));
362
+  console.log();
363
+
364
+  var len = body.byteLength + (csrRsaFoot.length/2) + sig.byteLength;
365
+  console.log('headlen', h(len));
366
+  var head = csrHead.replace(/{[^}]+}/, h(len));
367
+  var ab = new Uint8Array(new ArrayBuffer(4 + len));
368
+  var i = 0;
369
+  fromHex(head).forEach(function (b) {
370
+    ab[i] = b;
371
+    i += 1;
372
+  });
373
+  body.forEach(function (b) {
374
+    ab[i] = b;
375
+    i += 1;
376
+  });
377
+  fromHex(csrRsaFoot).forEach(function (b) {
378
+    ab[i] = b;
379
+    i += 1;
380
+  });
381
+  new Uint8Array(sig2).forEach(function (b) {
382
+    ab[i] = b;
383
+    i += 1;
384
+  });
385
+
386
+  console.log("Whole enchilada:", pubkey.toHex(ab.buffer) === pubkey.toHex(csrFull));
387
+  console.log(pubkey.toHex(ab.buffer));
388
+  console.log();
389
+
390
+  // subject + 2-byte altname headers + altnames themselves + public key
391
+  //var extralen = domains[0].length + (domains.length * 2) + domains.join('').length + csrpub.byteLength;
392
+  //var foot = csrRsaFoot.replace(/xxxx/g, (csrpub.byteLength + 1));
393
+  //var head = csrHead.replace(/xxxx/);
394
+  return false;
395
+}
396
+
397
+console.log();
398
+console.log("CSR");
399
+console.log(pubkey.toHex(csrFull));
400
+console.log();
401
+console.log(pubkey.toHex(csrbody.slice(0, csrbody.byteLength - sig.byteLength)));
402
+console.log();
403
+console.log();
404
+//signagain(csrbody);

Loading…
Cancel
Save