Browse Source

appears to work

AJ ONeal 7 months ago
parent
commit
510a367135
4 changed files with 132 additions and 31 deletions
  1. 8
    5
      index.html
  2. 1
    1
      install.sh
  3. 73
    8
      js/app.js
  4. 50
    17
      js/bacme.js

+ 8
- 5
index.html View File

@@ -120,19 +120,22 @@
120 120
       <button type="submit">Next</button>
121 121
     </form>
122 122
 
123
-    <!-- Step 4 Process Challanges -->
123
+    <!-- Step 5 Get Certs -->
124 124
     <form class="js-acme-form js-acme-form-download">
125 125
       <label>privkey.pem</label>
126
-      <textarea>-</textarea>
126
+      <textarea class="js-privkey">-</textarea>
127 127
 
128 128
       <label>fullchain.pem</label>
129
-      <textarea>-</textarea>
129
+      <textarea class="js-fullchain">-</textarea>
130 130
 
131
+      <!--
132
+        TODO
131 133
       <label>cert.pem</label>
132
-      <textarea>-</textarea>
134
+      <textarea class="js-cert">-</textarea>
133 135
 
134 136
       <label>chain.pem</label>
135
-      <textarea>-</textarea>
137
+      <textarea class="js-chain">-</textarea>
138
+      -->
136 139
 
137 140
       <button type="button">Download SSL Certificates</button>
138 141
 

+ 1
- 1
install.sh View File

@@ -10,5 +10,5 @@ popd
10 10
 
11 11
 mkdir -p js/browser-csr/v1.0.0-alpha/
12 12
 pushd js/browser-csr/v1.0.0-alpha/
13
-  wget -c https://git.coolaj86.com/coolaj86/browser-csr.js/raw/commit/c513a862a4e016794da800f0c2eec858b80837ab/csr.js
13
+  wget -c https://git.coolaj86.com/coolaj86/browser-csr.js/raw/commit/01cdc0e91b5bf03f12e1b25b4129e3cde927987c/csr.js
14 14
 popd

+ 73
- 8
js/app.js View File

@@ -30,7 +30,9 @@
30 30
     });
31 31
   });
32 32
   function updateChallengeType() {
33
-    var input = this || $qs('.js-acme-challenge-type');
33
+    var input = this || Array.prototype.filter.call(
34
+      $qsa('.js-acme-challenge-type'), function ($el) { return $el.checked; }
35
+    )[0];
34 36
     console.log('ch type radio:', input.value);
35 37
     $qs('.js-acme-table-wildcard').hidden = true;
36 38
     $qs('.js-acme-table-http-01').hidden = true;
@@ -200,7 +202,6 @@
200 202
                       , dnsAnswer: dnsAuth.answer
201 203
                       };
202 204
 
203
-                      obj[c.type].push(data);
204 205
                       console.log('');
205 206
                       console.log('CHALLENGE');
206 207
                       console.log(claim);
@@ -258,9 +259,6 @@
258 259
     $qs('.js-acme-form-challenges').hidden = false;
259 260
   };
260 261
   steps[3].submit = function () {
261
-    // for now just show the next page immediately (its a spinner)
262
-    console.log("MAGIC STEP NUMBER is:", i);
263
-
264 262
     var chType;
265 263
     Array.prototype.some.call($qsa('.js-acme-challenge-type'), function ($el) {
266 264
       if ($el.checked) {
@@ -283,6 +281,7 @@
283 281
         });
284 282
       });
285 283
     });
284
+    console.log("INFO.challenges !!!!!", info.challenges);
286 285
 
287 286
     var results = [];
288 287
     function nextChallenge() {
@@ -294,6 +293,7 @@
294 293
       });
295 294
     }
296 295
 
296
+    // for now just show the next page immediately (its a spinner)
297 297
     steps[i]();
298 298
     return nextChallenge().then(function (results) {
299 299
       console.log('challenge status:', results);
@@ -366,6 +366,7 @@
366 366
 
367 367
     return p.then(function (_serverJwk) {
368 368
       serverJwk = _serverJwk;
369
+      info.serverJwk = serverJwk;
369 370
       // { serverJwk, domains }
370 371
       return BACME.orders.generateCsr({
371 372
         serverJwk: serverJwk
@@ -373,7 +374,7 @@
373 374
           return ident.value;
374 375
         })
375 376
       }).then(function (csrweb64) {
376
-        return BACME.order.finalize({
377
+        return BACME.orders.finalize({
377 378
           csr: csrweb64
378 379
         , jwk: info.jwk
379 380
         , finalizeUrl: info.finalizeUrl
@@ -384,7 +385,7 @@
384 385
           return new Promise(function (resolve) {
385 386
             setTimeout(resolve, 1000);
386 387
           }).then(function () {
387
-            return BACME.order.check({ orderUrl: info.orderUrl });
388
+            return BACME.orders.check({ orderUrl: info.orderUrl });
388 389
           }).then(function (reply) {
389 390
             if ('processing' === reply) {
390 391
               return checkCert();
@@ -395,10 +396,74 @@
395 396
 
396 397
         return checkCert();
397 398
       }).then(function (reply) {
398
-        return BACME.order.receive({ certificateUrl: reply.certificate });
399
+        return BACME.orders.receive({ certificateUrl: reply.certificate });
399 400
       }).then(function (certs) {
400 401
         console.log('WINNING!');
401 402
         console.log(certs);
403
+        $qs('.js-fullchain').value = certs;
404
+
405
+        // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
406
+				function spkiToPEM(keydata){
407
+						var keydataS = arrayBufferToString(keydata);
408
+						var keydataB64 = window.btoa(keydataS);
409
+						var keydataB64Pem = formatAsPem(keydataB64);
410
+						return keydataB64Pem;
411
+				}
412
+
413
+				function arrayBufferToString( buffer ) {
414
+						var binary = '';
415
+						var bytes = new Uint8Array( buffer );
416
+						var len = bytes.byteLength;
417
+						for (var i = 0; i < len; i++) {
418
+								binary += String.fromCharCode( bytes[ i ] );
419
+						}
420
+						return binary;
421
+				}
422
+
423
+
424
+				function formatAsPem(str) {
425
+						var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n';
426
+
427
+						while(str.length > 0) {
428
+								finalString += str.substring(0, 64) + '\n';
429
+								str = str.substring(64);
430
+						}
431
+
432
+						finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----';
433
+
434
+						return finalString;
435
+				}
436
+
437
+        var wcOpts;
438
+        var pemName;
439
+        if (/^R/.test(info.serverJwk.kty)) {
440
+          pemName = 'RSA';
441
+          wcOpts = {
442
+            name: "RSASSA-PKCS1-v1_5"
443
+          , hash: { name: "SHA-256" }
444
+          };
445
+        } else {
446
+          pemName = 'EC';
447
+          wcOpts = {
448
+            name: "ECDSA"
449
+          , namedCurve: "P-256"
450
+          }
451
+        }
452
+				return crypto.subtle.importKey(
453
+          "jwk"
454
+        , info.serverJwk
455
+        , wcOpts
456
+        , true
457
+        , ["sign"]
458
+				).then(function (privateKey) {
459
+				  return window.crypto.subtle.exportKey("pkcs8", privateKey);
460
+				}).then (function (keydata) {
461
+					var pem = spkiToPEM(keydata);
462
+					$qs('.js-privkey').value = pem;
463
+          steps[i]();
464
+				}).catch(function(err){
465
+					console.error(err);
466
+				});
402 467
       });
403 468
     });
404 469
   };

+ 50
- 17
js/bacme.js View File

@@ -129,13 +129,24 @@ var textEncoder = new TextEncoder();
129 129
 BACME._importKey = function (jwk) {
130 130
   var alg; // I think the 256 refers to the hash
131 131
   var wcOpts = {};
132
-  var extractable = false;
132
+  var extractable = true; // TODO make optionally false?
133
+  var priv = jwk;
134
+  var pub;
133 135
 
134 136
   // ECDSA
135 137
   if (/^EC/i.test(jwk.kty)) {
136 138
     wcOpts.name = 'ECDSA';
137 139
     wcOpts.namedCurve = jwk.crv;
138 140
     alg = 'ES256';
141
+    pub = {
142
+      crv: priv.crv
143
+    , kty: priv.kty
144
+    , x: priv.x
145
+    , y: priv.y
146
+    };
147
+    if (!priv.d) {
148
+      priv = null;
149
+    }
139 150
   }
140 151
 
141 152
   // RSA
@@ -143,28 +154,50 @@ BACME._importKey = function (jwk) {
143 154
     wcOpts.name = 'RSASSA-PKCS1-v1_5';
144 155
     wcOpts.hash = { name: "SHA-256" };
145 156
     alg = 'RS256';
157
+    pub = {
158
+      e: priv.e
159
+    , kty: priv.kty
160
+    , n: priv.n
161
+    }
162
+    if (!priv.p) {
163
+      priv = null;
164
+    }
146 165
   }
147 166
 
148 167
   return window.crypto.subtle.importKey(
149 168
     "jwk"
150
-  , jwk
169
+  , pub
151 170
 	, wcOpts
152 171
   , extractable
153
-  , [ "sign"/*, "verify"*/ ]
154
-  ).then(function (keypair) {
155
-    return {
156
-      wcKey: keypair
157
-    , meta: {
158
-        alg: alg
159
-      , name: wcOpts.name
160
-      , hash: wcOpts.hash
161
-      }
162
-    , jwk: jwk
163
-    };
172
+  , [ "verify" ]
173
+  ).then(function (publicKey) {
174
+    function give(privateKey) {
175
+      return {
176
+        wcPub: publicKey
177
+      , wcKey: privateKey
178
+      , wcKeypair: { publicKey: publicKey, privateKey: privateKey }
179
+      , meta: {
180
+          alg: alg
181
+        , name: wcOpts.name
182
+        , hash: wcOpts.hash
183
+        }
184
+      , jwk: jwk
185
+      };
186
+    }
187
+    if (!priv) {
188
+      return give();
189
+    }
190
+    return window.crypto.subtle.importKey(
191
+      "jwk"
192
+    , priv
193
+    , wcOpts
194
+    , extractable
195
+    , [ "sign"/*, "verify"*/ ]
196
+    ).then(give);
164 197
   });
165 198
 };
166 199
 BACME._sign = function (opts) {
167
-  var wcPrivKey = opts.abstractKey.wcKey;
200
+  var wcPrivKey = opts.abstractKey.wcKeypair.privateKey;
168 201
   var wcOpts = opts.abstractKey.meta;
169 202
   var alg = opts.abstractKey.meta.alg; // I think the 256 refers to the hash
170 203
   var signHash;
@@ -508,6 +541,7 @@ BACME.challenges.accept = function (opts) {
508 541
 		).then(function (resp) {
509 542
       BACME._logHeaders(resp);
510 543
 			nonce = resp.headers.get('replay-nonce');
544
+      console.log("ACCEPT NONCE:", nonce);
511 545
 
512 546
 			return resp.json().then(function (reply) {
513 547
         challengePollUrl = reply.url;
@@ -523,7 +557,6 @@ BACME.challenges.accept = function (opts) {
523 557
 BACME.challenges.check = function (opts) {
524 558
 	return window.fetch(opts.challengePollUrl, { mode: 'cors' }).then(function (resp) {
525 559
     BACME._logHeaders(resp);
526
-		nonce = resp.headers.get('replay-nonce');
527 560
 
528 561
 		return resp.json().then(function (reply) {
529 562
 			challengePollUrl = reply.url;
@@ -566,7 +599,7 @@ BACME.domains.generateKeypair = function () {
566 599
 // { serverJwk, domains }
567 600
 BACME.orders.generateCsr = function (opts) {
568 601
   return BACME._importKey(opts.serverJwk).then(function (abstractKey) {
569
-    return Promise.resolve(CSR.generate({ keypair: abstractKey.wcKey, domains: opts.domains }));
602
+    return Promise.resolve(CSR.generate({ keypair: abstractKey.wcKeypair, domains: opts.domains }));
570 603
   });
571 604
 };
572 605
 
@@ -621,7 +654,7 @@ BACME.orders.receive = function (opts) {
621 654
     BACME._logHeaders(resp);
622 655
     nonce = resp.headers.get('replay-nonce');
623 656
 
624
-    return resp.json().then(function (reply) {
657
+    return resp.text().then(function (reply) {
625 658
       BACME._logBody(reply);
626 659
 
627 660
       return reply;