tested working in firefox
This commit is contained in:
		
							parent
							
								
									09ff0b3adc
								
							
						
					
					
						commit
						268f83b49e
					
				| @ -40,7 +40,7 @@ | |||||||
|     <link rel="preload" href="./js/pkijs.org/v1.3.33/x509_schema.js" as="script"> |     <link rel="preload" href="./js/pkijs.org/v1.3.33/x509_schema.js" as="script"> | ||||||
|     <link rel="preload" href="./js/pkijs.org/v1.3.33/x509_simpl.js" as="script"> |     <link rel="preload" href="./js/pkijs.org/v1.3.33/x509_simpl.js" as="script"> | ||||||
|     <link rel="preload" href="./js/browser-csr/v1.0.0-alpha/csr.js" as="script"> |     <link rel="preload" href="./js/browser-csr/v1.0.0-alpha/csr.js" as="script"> | ||||||
|      | 
 | ||||||
|   </head> |   </head> | ||||||
|   <body hidden> |   <body hidden> | ||||||
|     <!-- let's define our SVG that we will reuse --> |     <!-- let's define our SVG that we will reuse --> | ||||||
| @ -207,6 +207,7 @@ | |||||||
|               <div id="js-acme-ver-uri" class="acme-ver-uri">..loading</div> |               <div id="js-acme-ver-uri" class="acme-ver-uri">..loading</div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="js-acme-verification-dns-01"> |             <div class="js-acme-verification-dns-01"> | ||||||
|  |               <h3>Set this DNS Record</h3> | ||||||
|               <div class="acme-ver-dns-label">Hostname</div> |               <div class="acme-ver-dns-label">Hostname</div> | ||||||
|               <div id="js-acme-ver-hostname">loading...</div> |               <div id="js-acme-ver-hostname">loading...</div> | ||||||
|               <div class="acme-ver-dns-label">TXT Host</div> |               <div class="acme-ver-dns-label">TXT Host</div> | ||||||
|  | |||||||
							
								
								
									
										156
									
								
								app/js/app.js
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								app/js/app.js
									
									
									
									
									
								
							| @ -2,7 +2,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
|   /*global URLSearchParams,Headers*/ |   /*global URLSearchParams,Headers*/ | ||||||
|   var BROWSER_SUPPORTS_ECDSA = navigator.userAgent.toLowerCase().indexOf('firefox') === -1; |   var BROWSER_SUPPORTS_ECDSA; | ||||||
|   var $qs = function (s) { return window.document.querySelector(s); }; |   var $qs = function (s) { return window.document.querySelector(s); }; | ||||||
|   var $qsa = function (s) { return window.document.querySelectorAll(s); }; |   var $qsa = function (s) { return window.document.querySelectorAll(s); }; | ||||||
|   var info = {}; |   var info = {}; | ||||||
| @ -31,16 +31,50 @@ | |||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |   function testRsaSupport() { | ||||||
|  |     var opts = { | ||||||
|  |       type: 'RSA' | ||||||
|  |     , bitlength: '2048' | ||||||
|  |     }; | ||||||
|  |     return BACME.accounts.generateKeypair(opts).then(function (jwk) { | ||||||
|  |       return crypto.subtle.importKey( | ||||||
|  |         "jwk" | ||||||
|  |       , jwk | ||||||
|  |       , { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } } | ||||||
|  |       , true | ||||||
|  |       , ["sign"] | ||||||
|  |       ).then(function (privateKey) { | ||||||
|  |         return window.crypto.subtle.exportKey("pkcs8", privateKey); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|   testEcdsaSupport().then(function () { |   testEcdsaSupport().then(function () { | ||||||
|     console.log("supports ECDSA"); |     console.info("[crypto] ECDSA is supported"); | ||||||
|     BROWSER_SUPPORTS_ECDSA = true; |     BROWSER_SUPPORTS_ECDSA = true; | ||||||
|  |     localStorage.setItem('version', '1'); | ||||||
|     return true; |     return true; | ||||||
|   }).catch(function () { |   }).catch(function () { | ||||||
|     console.log("DOES NOT supports ECDSA"); |     console.warn("[crypto] ECDSA is NOT fully supported"); | ||||||
|     BROWSER_SUPPORTS_ECDSA = false; |     BROWSER_SUPPORTS_ECDSA = false; | ||||||
|  | 
 | ||||||
|  |     // fix previous firefox browsers
 | ||||||
|  |     if (!localStorage.getItem('version')) { | ||||||
|  |       localStorage.clear(); | ||||||
|  |       localStorage.getItem('version', '1'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // DO NOT RETURN HERE
 | ||||||
|  |     testRsaSupport().then(function () { | ||||||
|  |       console.info('[crypto] RSA is supported'); | ||||||
|  |     }).catch(function (err) { | ||||||
|  |       console.error('[crypto] could not use either EC nor RSA.'); | ||||||
|  |       console.error(err); | ||||||
|  |       window.alert("Your browser is cryptography support (neither RSA or EC is usable). Please use Chrome, Firefox, or Safari."); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // RETURN HERE
 | ||||||
|     return false; |     return false; | ||||||
|   }); |   }); | ||||||
|   // TODO test RSA support
 |  | ||||||
| 
 | 
 | ||||||
|   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; |   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; | ||||||
|   function updateApiType() { |   function updateApiType() { | ||||||
| @ -83,7 +117,10 @@ | |||||||
|     var j = i; |     var j = i; | ||||||
|     i += 1; |     i += 1; | ||||||
| 
 | 
 | ||||||
|     steps[j].submit(ev); |     return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) { | ||||||
|  |       console.error(err); | ||||||
|  |       window.alert.error("Something went wrong. It's our fault not yours. Please email aj@greenlock.domains and let him know that 'step " + j + "' failed."); | ||||||
|  |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   $qsa('.js-acme-form').forEach(function ($el) { |   $qsa('.js-acme-form').forEach(function ($el) { | ||||||
| @ -355,8 +392,9 @@ | |||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|     }).catch(function (err) { |     }).catch(function (err) { | ||||||
|       console.error('Step \'' + i + '\' Error:'); |       console.error('Step \'\' Error:'); | ||||||
|       console.error(err, err.stack); |       console.error(err, err.stack); | ||||||
|  |       window.alert("An error happened at Step " + i + ", but it's not your fault. Email aj@greenlock.domains and let him know."); | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -421,9 +459,22 @@ | |||||||
|               if ('pending' === poll.status) { |               if ('pending' === poll.status) { | ||||||
|                 return true; |                 return true; | ||||||
|               } |               } | ||||||
|  | 
 | ||||||
|  |               if ('invalid' === poll.status) { | ||||||
|  |                 allsWell = false; | ||||||
|  |                 window.alert("verification failed:" + poll.error.detail); | ||||||
|  |                 return; | ||||||
|  |               } | ||||||
|  | 
 | ||||||
|  |               if (poll.error) { | ||||||
|  |                 window.alert("verification failed:" + poll.error.detail); | ||||||
|  |                 return; | ||||||
|  |               } | ||||||
|  | 
 | ||||||
|               if ('valid' !== poll.status) { |               if ('valid' !== poll.status) { | ||||||
|                 allsWell = false; |                 allsWell = false; | ||||||
|                 console.warn('BAD POLL STATUS', poll); |                 console.warn('BAD POLL STATUS', poll); | ||||||
|  |                 window.alert("unknown error: " + JSON.stringify(poll, null, 2)); | ||||||
|               } |               } | ||||||
|               // TODO show status in HTML
 |               // TODO show status in HTML
 | ||||||
|             }); |             }); | ||||||
| @ -444,6 +495,38 @@ | |||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
 | ||||||
|  |   function spkiToPEM(keydata, pemName){ | ||||||
|  |       var keydataS = arrayBufferToString(keydata); | ||||||
|  |       var keydataB64 = window.btoa(keydataS); | ||||||
|  |       var keydataB64Pem = formatAsPem(keydataB64, pemName); | ||||||
|  |       return keydataB64Pem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function arrayBufferToString( buffer ) { | ||||||
|  |       var binary = ''; | ||||||
|  |       var bytes = new Uint8Array( buffer ); | ||||||
|  |       var len = bytes.byteLength; | ||||||
|  |       for (var i = 0; i < len; i++) { | ||||||
|  |           binary += String.fromCharCode( bytes[ i ] ); | ||||||
|  |       } | ||||||
|  |       return binary; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   function formatAsPem(str, pemName) { | ||||||
|  |       var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n'; | ||||||
|  | 
 | ||||||
|  |       while(str.length > 0) { | ||||||
|  |           finalString += str.substring(0, 64) + '\n'; | ||||||
|  |           str = str.substring(64); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----'; | ||||||
|  | 
 | ||||||
|  |       return finalString; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // spinner
 |   // spinner
 | ||||||
|   steps[4] = function () { |   steps[4] = function () { | ||||||
|     updateProgress(1); |     updateProgress(1); | ||||||
| @ -460,16 +543,10 @@ | |||||||
|     function createKeypair() { |     function createKeypair() { | ||||||
|       var opts; |       var opts; | ||||||
| 
 | 
 | ||||||
|       if(BROWSER_SUPPORTS_ECDSA) { |       if (BROWSER_SUPPORTS_ECDSA) { | ||||||
|         opts = { |         opts = { type: 'ECDSA', bitlength: '256' }; | ||||||
|           type: 'ECDSA' |  | ||||||
|         , bitlength: '256' |  | ||||||
|         }; |  | ||||||
|       } else { |       } else { | ||||||
|         opts = { |         opts = { type: 'RSA', bitlength: '2048' }; | ||||||
|           type: 'RSA' |  | ||||||
|         , bitlength: '2048' |  | ||||||
|         }; |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return BACME.domains.generateKeypair(opts).then(function (serverJwk) { |       return BACME.domains.generateKeypair(opts).then(function (serverJwk) { | ||||||
| @ -524,52 +601,14 @@ | |||||||
|         $qs("#js-download-fullchain-link").href = |         $qs("#js-download-fullchain-link").href = | ||||||
|           "data:text/octet-stream;base64," + window.btoa(certs); |           "data:text/octet-stream;base64," + window.btoa(certs); | ||||||
| 
 | 
 | ||||||
|         // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
 |  | ||||||
|         function spkiToPEM(keydata){ |  | ||||||
|             var keydataS = arrayBufferToString(keydata); |  | ||||||
|             var keydataB64 = window.btoa(keydataS); |  | ||||||
|             var keydataB64Pem = formatAsPem(keydataB64); |  | ||||||
|             return keydataB64Pem; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         function arrayBufferToString( buffer ) { |  | ||||||
|             var binary = ''; |  | ||||||
|             var bytes = new Uint8Array( buffer ); |  | ||||||
|             var len = bytes.byteLength; |  | ||||||
|             for (var i = 0; i < len; i++) { |  | ||||||
|                 binary += String.fromCharCode( bytes[ i ] ); |  | ||||||
|             } |  | ||||||
|             return binary; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         function formatAsPem(str) { |  | ||||||
|             var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n'; |  | ||||||
| 
 |  | ||||||
|             while(str.length > 0) { |  | ||||||
|                 finalString += str.substring(0, 64) + '\n'; |  | ||||||
|                 str = str.substring(64); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----'; |  | ||||||
| 
 |  | ||||||
|             return finalString; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         var wcOpts; |         var wcOpts; | ||||||
|         var pemName; |         var pemName; | ||||||
|         if (/^R/.test(info.serverJwk.kty)) { |         if (/^R/.test(info.serverJwk.kty)) { | ||||||
|           pemName = 'RSA'; |           pemName = 'RSA'; | ||||||
|           wcOpts = { |           wcOpts = { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }; | ||||||
|             name: "RSASSA-PKCS1-v1_5" |  | ||||||
|           , hash: { name: "SHA-256" } |  | ||||||
|           }; |  | ||||||
|         } else { |         } else { | ||||||
|           pemName = 'EC'; |           pemName = 'EC'; | ||||||
|           wcOpts = { |           wcOpts = { name: "ECDSA", namedCurve: "P-256" }; | ||||||
|             name: "ECDSA" |  | ||||||
|           , namedCurve: "P-256" |  | ||||||
|           }; |  | ||||||
|         } |         } | ||||||
|         return crypto.subtle.importKey( |         return crypto.subtle.importKey( | ||||||
|           "jwk" |           "jwk" | ||||||
| @ -580,15 +619,16 @@ | |||||||
|         ).then(function (privateKey) { |         ).then(function (privateKey) { | ||||||
|           return window.crypto.subtle.exportKey("pkcs8", privateKey); |           return window.crypto.subtle.exportKey("pkcs8", privateKey); | ||||||
|         }).then (function (keydata) { |         }).then (function (keydata) { | ||||||
|           var pem = spkiToPEM(keydata); |           var pem = spkiToPEM(keydata, pemName); | ||||||
|           $qs('#js-privkey').innerHTML = pem; |           $qs('#js-privkey').innerHTML = pem; | ||||||
|           $qs("#js-download-privkey-link").href = |           $qs("#js-download-privkey-link").href = | ||||||
|             "data:text/octet-stream;base64," + window.btoa(pem); |             "data:text/octet-stream;base64," + window.btoa(pem); | ||||||
|           steps[i](); |           steps[i](); | ||||||
|         }).catch(function(err){ |  | ||||||
|           console.error(err.toString()); |  | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|  |     }).catch(function (err) { | ||||||
|  |       console.error(err.toString()); | ||||||
|  |       window.alert("An error happened in the final step, but it's not your fault. Email aj@greenlock.domains and let him know."); | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -38,13 +38,16 @@ BACME._logBody = function (body) { | |||||||
| BACME.directory = function (opts) { | BACME.directory = function (opts) { | ||||||
|   return webFetch(opts.directoryUrl || directoryUrl, { mode: 'cors' }).then(function (resp) { |   return webFetch(opts.directoryUrl || directoryUrl, { mode: 'cors' }).then(function (resp) { | ||||||
|     BACME._logHeaders(resp); |     BACME._logHeaders(resp); | ||||||
|     return resp.json().then(function (body) { |     return resp.json().then(function (reply) { | ||||||
|       directory = body; |       if (/error/.test(reply.type)) { | ||||||
|  |         return Promise.reject(new Error(reply.detail || reply.type)); | ||||||
|  |       } | ||||||
|  |       directory = reply; | ||||||
|       nonceUrl = directory.newNonce || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce'; |       nonceUrl = directory.newNonce || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce'; | ||||||
|       accountUrl = directory.newAccount || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-account'; |       accountUrl = directory.newAccount || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-account'; | ||||||
|       orderUrl = directory.newOrder || "https://acme-staging-v02.api.letsencrypt.org/acme/new-order"; |       orderUrl = directory.newOrder || "https://acme-staging-v02.api.letsencrypt.org/acme/new-order"; | ||||||
|       BACME._logBody(body); |       BACME._logBody(reply); | ||||||
|       return body; |       return reply; | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| @ -227,18 +230,30 @@ BACME.accounts.sign = function (opts) { | |||||||
|       payloadJson |       payloadJson | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     // TODO RSA
 |  | ||||||
|     var protectedJson = |     var protectedJson = | ||||||
|       { nonce: opts.nonce |       { nonce: opts.nonce | ||||||
|       , url: accountUrl |       , url: accountUrl | ||||||
|       , alg: abstractKey.meta.alg |       , alg: abstractKey.meta.alg | ||||||
|       , jwk: { |       , jwk: null | ||||||
|           kty: opts.jwk.kty |  | ||||||
|         , crv: opts.jwk.crv |  | ||||||
|         , x: opts.jwk.x |  | ||||||
|         , y: opts.jwk.y |  | ||||||
|         } |  | ||||||
|       }; |       }; | ||||||
|  | 
 | ||||||
|  |     if (/EC/i.test(opts.jwk.kty)) { | ||||||
|  |       protectedJson.jwk = { | ||||||
|  |         crv: opts.jwk.crv | ||||||
|  |       , kty: opts.jwk.kty | ||||||
|  |       , x: opts.jwk.x | ||||||
|  |       , y: opts.jwk.y | ||||||
|  |       }; | ||||||
|  |     } else if (/RS/i.test(opts.jwk.kty)) { | ||||||
|  |       protectedJson.jwk = { | ||||||
|  |         e: opts.jwk.e | ||||||
|  |       , kty: opts.jwk.kty | ||||||
|  |       , n: opts.jwk.n | ||||||
|  |       }; | ||||||
|  |     } else { | ||||||
|  |       return Promise.reject(new Error("[acme.accounts.sign] unsupported key type '" + opts.jwk.kty + "'")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     console.log('protected:'); |     console.log('protected:'); | ||||||
|     console.log(protectedJson); |     console.log(protectedJson); | ||||||
|     var protected64 = BACME._jsto64( |     var protected64 = BACME._jsto64( | ||||||
| @ -486,9 +501,7 @@ var challengePollUrl; | |||||||
| 
 | 
 | ||||||
| // { jwk, challengeUrl, accountId (kid) }
 | // { jwk, challengeUrl, accountId (kid) }
 | ||||||
| BACME.challenges.accept = function (opts) { | BACME.challenges.accept = function (opts) { | ||||||
|   var payload64 = BACME._jsto64( |   var payload64 = BACME._jsto64({}); | ||||||
|     {} |  | ||||||
|   ); |  | ||||||
| 
 | 
 | ||||||
|   return BACME._importKey(opts.jwk).then(function (abstractKey) { |   return BACME._importKey(opts.jwk).then(function (abstractKey) { | ||||||
|     var protected64 = BACME._jsto64( |     var protected64 = BACME._jsto64( | ||||||
| @ -530,6 +543,9 @@ BACME.challenges.check = function (opts) { | |||||||
|     BACME._logHeaders(resp); |     BACME._logHeaders(resp); | ||||||
| 
 | 
 | ||||||
|     return resp.json().then(function (reply) { |     return resp.json().then(function (reply) { | ||||||
|  |       if (/error/.test(reply.type)) { | ||||||
|  |         return Promise.reject(new Error(reply.detail || reply.type)); | ||||||
|  |       } | ||||||
|       challengePollUrl = reply.url; |       challengePollUrl = reply.url; | ||||||
| 
 | 
 | ||||||
|       BACME._logBody(reply); |       BACME._logBody(reply); | ||||||
| @ -630,6 +646,9 @@ BACME.orders.finalize = function (opts) { | |||||||
|       nonce = resp.headers.get('replay-nonce'); |       nonce = resp.headers.get('replay-nonce'); | ||||||
| 
 | 
 | ||||||
|       return resp.json().then(function (reply) { |       return resp.json().then(function (reply) { | ||||||
|  |         if (/error/.test(reply.type)) { | ||||||
|  |           return Promise.reject(new Error(reply.detail || reply.type)); | ||||||
|  |         } | ||||||
|         certificateUrl = reply.certificate; |         certificateUrl = reply.certificate; | ||||||
|         BACME._logBody(reply); |         BACME._logBody(reply); | ||||||
| 
 | 
 | ||||||
| @ -667,6 +686,9 @@ BACME.orders.check = function (opts) { | |||||||
|     BACME._logHeaders(resp); |     BACME._logHeaders(resp); | ||||||
| 
 | 
 | ||||||
|     return resp.json().then(function (reply) { |     return resp.json().then(function (reply) { | ||||||
|  |       if (/error/.test(reply.type)) { | ||||||
|  |         return Promise.reject(new Error(reply.detail || reply.type)); | ||||||
|  |       } | ||||||
|       BACME._logBody(reply); |       BACME._logBody(reply); | ||||||
| 
 | 
 | ||||||
|       return reply; |       return reply; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user