tested working in firefox
This commit is contained in:
		
							parent
							
								
									09ff0b3adc
								
							
						
					
					
						commit
						268f83b49e
					
				| @ -207,6 +207,7 @@ | ||||
|               <div id="js-acme-ver-uri" class="acme-ver-uri">..loading</div> | ||||
|             </div> | ||||
|             <div class="js-acme-verification-dns-01"> | ||||
|               <h3>Set this DNS Record</h3> | ||||
|               <div class="acme-ver-dns-label">Hostname</div> | ||||
|               <div id="js-acme-ver-hostname">loading...</div> | ||||
|               <div class="acme-ver-dns-label">TXT Host</div> | ||||
|  | ||||
							
								
								
									
										158
									
								
								app/js/app.js
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								app/js/app.js
									
									
									
									
									
								
							| @ -2,7 +2,7 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
|   /*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 $qsa = function (s) { return window.document.querySelectorAll(s); }; | ||||
|   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 () { | ||||
|     console.log("supports ECDSA"); | ||||
|     console.info("[crypto] ECDSA is supported"); | ||||
|     BROWSER_SUPPORTS_ECDSA = true; | ||||
|     localStorage.setItem('version', '1'); | ||||
|     return true; | ||||
|   }).catch(function () { | ||||
|     console.log("DOES NOT supports ECDSA"); | ||||
|     console.warn("[crypto] ECDSA is NOT fully supported"); | ||||
|     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; | ||||
|   }); | ||||
|   // TODO test RSA support
 | ||||
| 
 | ||||
|   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; | ||||
|   function updateApiType() { | ||||
| @ -83,7 +117,10 @@ | ||||
|     var j = i; | ||||
|     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) { | ||||
| @ -355,8 +392,9 @@ | ||||
|         }); | ||||
|       }); | ||||
|     }).catch(function (err) { | ||||
|       console.error('Step \'' + i + '\' Error:'); | ||||
|       console.error('Step \'\' Error:'); | ||||
|       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) { | ||||
|                 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) { | ||||
|                 allsWell = false; | ||||
|                 console.warn('BAD POLL STATUS', poll); | ||||
|                 window.alert("unknown error: " + JSON.stringify(poll, null, 2)); | ||||
|               } | ||||
|               // 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
 | ||||
|   steps[4] = function () { | ||||
|     updateProgress(1); | ||||
| @ -460,16 +543,10 @@ | ||||
|     function createKeypair() { | ||||
|       var opts; | ||||
| 
 | ||||
|       if(BROWSER_SUPPORTS_ECDSA) { | ||||
|         opts = { | ||||
|           type: 'ECDSA' | ||||
|         , bitlength: '256' | ||||
|         }; | ||||
|       if (BROWSER_SUPPORTS_ECDSA) { | ||||
|         opts = { type: 'ECDSA', bitlength: '256' }; | ||||
|       } else { | ||||
|         opts = { | ||||
|           type: 'RSA' | ||||
|         , bitlength: '2048' | ||||
|         }; | ||||
|         opts = { type: 'RSA', bitlength: '2048' }; | ||||
|       } | ||||
| 
 | ||||
|       return BACME.domains.generateKeypair(opts).then(function (serverJwk) { | ||||
| @ -524,52 +601,14 @@ | ||||
|         $qs("#js-download-fullchain-link").href = | ||||
|           "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 pemName; | ||||
|         if (/^R/.test(info.serverJwk.kty)) { | ||||
|           pemName = 'RSA'; | ||||
|           wcOpts = { | ||||
|             name: "RSASSA-PKCS1-v1_5" | ||||
|           , hash: { name: "SHA-256" } | ||||
|           }; | ||||
|           wcOpts = { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }; | ||||
|         } else { | ||||
|           pemName = 'EC'; | ||||
|           wcOpts = { | ||||
|             name: "ECDSA" | ||||
|           , namedCurve: "P-256" | ||||
|           }; | ||||
|           wcOpts = { name: "ECDSA", namedCurve: "P-256" }; | ||||
|         } | ||||
|         return crypto.subtle.importKey( | ||||
|           "jwk" | ||||
| @ -580,15 +619,16 @@ | ||||
|         ).then(function (privateKey) { | ||||
|           return window.crypto.subtle.exportKey("pkcs8", privateKey); | ||||
|         }).then (function (keydata) { | ||||
|           var pem = spkiToPEM(keydata); | ||||
|           var pem = spkiToPEM(keydata, pemName); | ||||
|           $qs('#js-privkey').innerHTML = pem; | ||||
|           $qs("#js-download-privkey-link").href = | ||||
|             "data:text/octet-stream;base64," + window.btoa(pem); | ||||
|           steps[i](); | ||||
|         }).catch(function(err){ | ||||
|         }); | ||||
|       }); | ||||
|     }).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) { | ||||
|   return webFetch(opts.directoryUrl || directoryUrl, { mode: 'cors' }).then(function (resp) { | ||||
|     BACME._logHeaders(resp); | ||||
|     return resp.json().then(function (body) { | ||||
|       directory = body; | ||||
|     return resp.json().then(function (reply) { | ||||
|       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'; | ||||
|       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"; | ||||
|       BACME._logBody(body); | ||||
|       return body; | ||||
|       BACME._logBody(reply); | ||||
|       return reply; | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
| @ -227,18 +230,30 @@ BACME.accounts.sign = function (opts) { | ||||
|       payloadJson | ||||
|     ); | ||||
| 
 | ||||
|     // TODO RSA
 | ||||
|     var protectedJson = | ||||
|       { nonce: opts.nonce | ||||
|       , url: accountUrl | ||||
|       , alg: abstractKey.meta.alg | ||||
|       , jwk: { | ||||
|           kty: opts.jwk.kty | ||||
|         , crv: opts.jwk.crv | ||||
|       , jwk: null | ||||
|       }; | ||||
| 
 | ||||
|     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(protectedJson); | ||||
|     var protected64 = BACME._jsto64( | ||||
| @ -486,9 +501,7 @@ var challengePollUrl; | ||||
| 
 | ||||
| // { jwk, challengeUrl, accountId (kid) }
 | ||||
| BACME.challenges.accept = function (opts) { | ||||
|   var payload64 = BACME._jsto64( | ||||
|     {} | ||||
|   ); | ||||
|   var payload64 = BACME._jsto64({}); | ||||
| 
 | ||||
|   return BACME._importKey(opts.jwk).then(function (abstractKey) { | ||||
|     var protected64 = BACME._jsto64( | ||||
| @ -530,6 +543,9 @@ BACME.challenges.check = function (opts) { | ||||
|     BACME._logHeaders(resp); | ||||
| 
 | ||||
|     return resp.json().then(function (reply) { | ||||
|       if (/error/.test(reply.type)) { | ||||
|         return Promise.reject(new Error(reply.detail || reply.type)); | ||||
|       } | ||||
|       challengePollUrl = reply.url; | ||||
| 
 | ||||
|       BACME._logBody(reply); | ||||
| @ -630,6 +646,9 @@ BACME.orders.finalize = function (opts) { | ||||
|       nonce = resp.headers.get('replay-nonce'); | ||||
| 
 | ||||
|       return resp.json().then(function (reply) { | ||||
|         if (/error/.test(reply.type)) { | ||||
|           return Promise.reject(new Error(reply.detail || reply.type)); | ||||
|         } | ||||
|         certificateUrl = reply.certificate; | ||||
|         BACME._logBody(reply); | ||||
| 
 | ||||
| @ -667,6 +686,9 @@ BACME.orders.check = function (opts) { | ||||
|     BACME._logHeaders(resp); | ||||
| 
 | ||||
|     return resp.json().then(function (reply) { | ||||
|       if (/error/.test(reply.type)) { | ||||
|         return Promise.reject(new Error(reply.detail || reply.type)); | ||||
|       } | ||||
|       BACME._logBody(reply); | ||||
| 
 | ||||
|       return reply; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user