WIP almost working
This commit is contained in:
		
							parent
							
								
									7484ffcd11
								
							
						
					
					
						commit
						19cc513174
					
				| @ -33,13 +33,8 @@ | |||||||
|     <link rel="preload" href="./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous"> |     <link rel="preload" href="./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous"> | ||||||
| 
 | 
 | ||||||
|     <link rel="preload" href="./fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2" as="font" crossorigin="anonymous"> |     <link rel="preload" href="./fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2" as="font" crossorigin="anonymous"> | ||||||
|     <link rel="preload" href="./js/bacme.js" as="script"> |     <link rel="preload" href="./js/bluecrypt-acme.js" as="script"> | ||||||
|     <link rel="preload" href="./js/app.js" as="script"> |     <link rel="preload" href="./js/greenlock.js" as="script"> | ||||||
|     <link rel="preload" href="./js/pkijs.org/v1.3.33/common.js" as="script"> |  | ||||||
|     <link rel="preload" href="./js/pkijs.org/v1.3.33/asn1.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/browser-csr/v1.0.0-alpha/csr.js" as="script"> |  | ||||||
| 
 | 
 | ||||||
|   </head> |   </head> | ||||||
|   <body hidden> |   <body hidden> | ||||||
| @ -342,14 +337,8 @@ | |||||||
|       <br> |       <br> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         <script src="./js/bacme.js"></script> |         <script src="./js/bluecrypt-acme.js"></script> | ||||||
|         <script src="./js/app.js"></script> |         <script src="./js/greenlock.js"></script> | ||||||
| 
 |  | ||||||
|         <script src="./js/pkijs.org/v1.3.33/common.js"></script> |  | ||||||
|         <script src="./js/pkijs.org/v1.3.33/asn1.js"></script> |  | ||||||
|         <script src="./js/pkijs.org/v1.3.33/x509_schema.js"></script> |  | ||||||
|         <script src="./js/pkijs.org/v1.3.33/x509_simpl.js"></script> |  | ||||||
|         <script src="./js/browser-csr/v1.0.0-alpha/csr.js"></script> |  | ||||||
| 
 | 
 | ||||||
|         <!-- Global site tag (gtag.js) - Google Analytics --> |         <!-- Global site tag (gtag.js) - Google Analytics --> | ||||||
|         <script async src="https://www.googletagmanager.com/gtag/js?id=UA-118745161-2"></script> |         <script async src="https://www.googletagmanager.com/gtag/js?id=UA-118745161-2"></script> | ||||||
|  | |||||||
| @ -2,8 +2,7 @@ | |||||||
| /* This Source Code Form is subject to the terms of the Mozilla Public | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||||
| ; | ;(function (exports) { | ||||||
| (function (exports) { |  | ||||||
| 
 | 
 | ||||||
| var Enc = exports.Enc = {}; | var Enc = exports.Enc = {}; | ||||||
| 
 | 
 | ||||||
| @ -1962,7 +1961,12 @@ ACME._getChallenges = function (me, options, authUrl) { | |||||||
|   , payload: '' |   , payload: '' | ||||||
|   , url: authUrl |   , url: authUrl | ||||||
|   }).then(function (resp) { |   }).then(function (resp) { | ||||||
|     return resp.body; |     // Pre-emptive rather than lazy for interfaces that need to show the challenges to the user first
 | ||||||
|  |     return ACME._challengesToAuth(me, options, resp.body, false).then(function (auths) { | ||||||
|  |       resp.body._rawChallenges = resp.body.challenges; | ||||||
|  |       resp.body.challenges = auths; | ||||||
|  |       return resp.body; | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| ACME._wait = function wait(ms) { | ACME._wait = function wait(ms) { | ||||||
| @ -1987,12 +1991,6 @@ ACME._testChallengeOptions = function () { | |||||||
|       "token": "test-" + chToken + "-1", |       "token": "test-" + chToken + "-1", | ||||||
|       "_wildcard": true |       "_wildcard": true | ||||||
|     } |     } | ||||||
|   , { |  | ||||||
|       "type": "tls-sni-01", |  | ||||||
|       "status": "pending", |  | ||||||
|       "url": "https://acme-staging-v02.example.com/2", |  | ||||||
|       "token": "test-" + chToken + "-2" |  | ||||||
|     } |  | ||||||
|   , { |   , { | ||||||
|       "type": "tls-alpn-01", |       "type": "tls-alpn-01", | ||||||
|       "status": "pending", |       "status": "pending", | ||||||
| @ -2010,47 +2008,49 @@ ACME._testChallenges = function (me, options) { | |||||||
|       challenges = challenges.filter(function (ch) { return ch._wildcard; }); |       challenges = challenges.filter(function (ch) { return ch._wildcard; }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var challenge = ACME._chooseChallenge(options, { challenges: challenges }); |     // The dry-run comes first in the spirit of "fail fast"
 | ||||||
|     if (!challenge) { |     // (and protecting against challenge failure rate limits)
 | ||||||
|       // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 |     var dryrun = true; | ||||||
|       var enabled = Object.keys(options.challenges).join(', ') || 'none'; |     var resp = { | ||||||
|       var suitable = challenges.map(function (r) { return r.type; }).join(', ') || 'none'; |       body: { | ||||||
|       return Promise.reject(new Error( |  | ||||||
|         "None of the challenge types that you've enabled ( " + enabled + " )" |  | ||||||
|           + " are suitable for validating the domain you've selected (" + identifierValue + ")." |  | ||||||
|           + " You must enable one of ( " + suitable + " )." |  | ||||||
|       )); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // TODO remove skipChallengeTest
 |  | ||||||
|     if (me.skipDryRun || me.skipChallengeTest) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if ('dns-01' === challenge.type) { |  | ||||||
|       // Give the nameservers a moment to propagate
 |  | ||||||
|       CHECK_DELAY = 1.5 * 1000; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return Promise.resolve().then(function () { |  | ||||||
|       var results = { |  | ||||||
|         identifier: { |         identifier: { | ||||||
|           type: "dns" |           type: "dns" | ||||||
|         , value: identifierValue.replace(/^\*\./, '') |         , value: identifierValue.replace(/^\*\./, '') | ||||||
|         } |         } | ||||||
|       , challenges: [ challenge ] |       , challenges: challenges | ||||||
|       , expires: new Date(Date.now() + (60 * 1000)).toISOString() |       , expires: new Date(Date.now() + (60 * 1000)).toISOString() | ||||||
|       , wildcard: identifierValue.includes('*.') || undefined |       , wildcard: identifierValue.includes('*.') || undefined | ||||||
|       }; |       } | ||||||
|  |     }; | ||||||
|  |     return ACME._challengesToAuth(me, options, resp.body, dryrun).then(function (auths) { | ||||||
|  |       resp.body._rawChallenges = resp.body.challenges; | ||||||
|  |       resp.body.challenges = auths; | ||||||
| 
 | 
 | ||||||
|       // The dry-run comes first in the spirit of "fail fast"
 |       var auth = ACME._chooseAuth(options, resp.body.challenges); | ||||||
|       // (and protecting against challenge failure rate limits)
 |       if (!auth) { | ||||||
|       var dryrun = true; |         // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 | ||||||
|       return ACME._challengeToAuth(me, options, results, challenge, dryrun).then(function (auth) { |         var enabled = Object.keys(options.challenges).join(', ') || 'none'; | ||||||
|         if (!me._canUse[auth.type]) { return; } |         var suitable = resp.body.challenges.map(function (r) { return r.type; }).join(', ') || 'none'; | ||||||
|         return ACME._setChallenge(me, options, auth).then(function () { |         return Promise.reject(new Error( | ||||||
|           return auth; |           "None of the challenge types that you've enabled ( " + enabled + " )" | ||||||
|         }); |             + " are suitable for validating the domain you've selected (" + identifierValue + ")." | ||||||
|  |             + " You must enable one of ( " + suitable + " )." | ||||||
|  |         )); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // TODO remove skipChallengeTest
 | ||||||
|  |       if (me.skipDryRun || me.skipChallengeTest) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if ('dns-01' === auth.type) { | ||||||
|  |         // Give the nameservers a moment to propagate
 | ||||||
|  |         CHECK_DELAY = 1.5 * 1000; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!me._canUse[auth.type]) { return; } | ||||||
|  |       return ACME._setChallenge(me, options, auth).then(function () { | ||||||
|  |         return auth; | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   })).then(function (auths) { |   })).then(function (auths) { | ||||||
| @ -2067,93 +2067,88 @@ ACME._testChallenges = function (me, options) { | |||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| ACME._chooseChallenge = function(options, results) { | ACME._chooseAuth = function(options, auths) { | ||||||
|   // For each of the challenge types that we support
 |   // For each of the challenge types that we support
 | ||||||
|   var challenge; |   var auth; | ||||||
|   var challengeTypes = Object.keys(options.challenges); |   var challengeTypes = Object.keys(options.challenges); | ||||||
|   // ordered from most to least preferred
 |   // ordered from most to least preferred
 | ||||||
|   challengeTypes = [ 'tls-alpn-01', 'http-01', 'dns-01' ].filter(function (chType) { |   challengeTypes = (options.challengePriority||[ 'tls-alpn-01', 'http-01', 'dns-01' ]).filter(function (chType) { | ||||||
|     return challengeTypes.includes(chType); |     return challengeTypes.includes(chType); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   /* |  | ||||||
|   // Lot's of error checking to inform the user of mistakes
 |  | ||||||
|   if (!(options.challengeTypes||[]).length) { |  | ||||||
|     options.challengeTypes = Object.keys(options.challenges||{}); |  | ||||||
|   } |  | ||||||
|   if (!options.challengeTypes.length) { |  | ||||||
|     options.challengeTypes = [ options.challengeType ].filter(Boolean); |  | ||||||
|   } |  | ||||||
|   if (options.challengeType) { |  | ||||||
|     options.challengeTypes.sort(function (a, b) { |  | ||||||
|       if (a === options.challengeType) { return -1; } |  | ||||||
|       if (b === options.challengeType) { return 1; } |  | ||||||
|       return 0; |  | ||||||
|     }); |  | ||||||
|     if (options.challengeType !== options.challengeTypes[0]) { |  | ||||||
|       return Promise.reject(new Error("options.challengeType is '" + options.challengeType + "'," |  | ||||||
|         + " which does not exist in the supplied types '" + options.challengeTypes.join(',') + "'")); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   // TODO check that all challengeTypes are represented in challenges
 |  | ||||||
|   if (!options.challengeTypes.length) { |  | ||||||
|     return Promise.reject(new Error("options.challengeTypes (string array) must be specified" |  | ||||||
|       + " (and in order of preferential priority).")); |  | ||||||
|   } |  | ||||||
|   */ |  | ||||||
|   challengeTypes.some(function (chType) { |   challengeTypes.some(function (chType) { | ||||||
|     // And for each of the challenge types that are allowed
 |     // And for each of the challenge types that are allowed
 | ||||||
|     return results.challenges.some(function (ch) { |     return auths.some(function (ch) { | ||||||
|       // Check to see if there are any matches
 |       // Check to see if there are any matches
 | ||||||
|       if (ch.type === chType) { |       if (ch.type === chType) { | ||||||
|         challenge = ch; |         auth = ch; | ||||||
|         return true; |         return true; | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   return challenge; |   return auth; | ||||||
| }; | }; | ||||||
| ACME._challengeToAuth = function (me, options, request, challenge, dryrun) { | ACME._challengesToAuth = function (me, options, request, dryrun) { | ||||||
|   // we don't poison the dns cache with our dummy request
 |   // we don't poison the dns cache with our dummy request
 | ||||||
|   var dnsPrefix = ACME.challengePrefixes['dns-01']; |   var dnsPrefix = ACME.challengePrefixes['dns-01']; | ||||||
|   if (dryrun) { |   if (dryrun) { | ||||||
|     dnsPrefix = dnsPrefix.replace('acme-challenge', 'greenlock-dryrun-' + ACME._prnd(4)); |     dnsPrefix = dnsPrefix.replace('acme-challenge', 'greenlock-dryrun-' + ACME._prnd(4)); | ||||||
|   } |   } | ||||||
|  |   var challengeTypes = Object.keys(options.challenges); | ||||||
| 
 | 
 | ||||||
|   var auth = {}; |  | ||||||
| 
 |  | ||||||
|   // straight copy from the new order response
 |  | ||||||
|   // { identifier, status, expires, challenges, wildcard }
 |  | ||||||
|   Object.keys(request).forEach(function (key) { |  | ||||||
|     auth[key] = request[key]; |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   // copy from the challenge we've chosen
 |  | ||||||
|   // { type, status, url, token }
 |  | ||||||
|   // (note the duplicate status overwrites the one above, but they should be the same)
 |  | ||||||
|   Object.keys(challenge).forEach(function (key) { |  | ||||||
|     // don't confused devs with the id url
 |  | ||||||
|     auth[key] = challenge[key]; |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   // batteries-included helpers
 |  | ||||||
|   auth.hostname = auth.identifier.value; |  | ||||||
|   // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
 |  | ||||||
|   auth.altname = ACME._untame(auth.identifier.value, auth.wildcard); |  | ||||||
|   return ACME._importKeypair(me, options.accountKeypair).then(function (pair) { |   return ACME._importKeypair(me, options.accountKeypair).then(function (pair) { | ||||||
|     return me.Keypairs.thumbprint({ jwk: pair.public }).then(function (thumb) { |     return me.Keypairs.thumbprint({ jwk: pair.public }).then(function (thumb) { | ||||||
|       auth.thumbprint = thumb; |       return Promise.all(request.challenges.map(function (challenge) { | ||||||
|       //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 |         // Don't do extra work for challenges that we can't satisfy
 | ||||||
|       auth.keyAuthorization = challenge.token + '.' + auth.thumbprint; |         if (!challengeTypes.includes(challenge.type)) { | ||||||
|       // conflicts with ACME challenge id url is already in use, so we call this challengeUrl instead
 |           return null; | ||||||
|       // TODO auth.http01Url ?
 |         } | ||||||
|       auth.challengeUrl = 'http://' + auth.identifier.value + ACME.challengePrefixes['http-01'] + '/' + auth.token; |  | ||||||
|       auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', ''); |  | ||||||
| 
 | 
 | ||||||
|       return Crypto._sha('sha256', auth.keyAuthorization).then(function (hash) { |         var auth = {}; | ||||||
|         auth.dnsAuthorization = hash; | 
 | ||||||
|         return auth; |         // straight copy from the new order response
 | ||||||
|  |         // { identifier, status, expires, challenges, wildcard }
 | ||||||
|  |         Object.keys(request).forEach(function (key) { | ||||||
|  |           auth[key] = request[key]; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // copy from the challenge we've chosen
 | ||||||
|  |         // { type, status, url, token }
 | ||||||
|  |         // (note the duplicate status overwrites the one above, but they should be the same)
 | ||||||
|  |         Object.keys(challenge).forEach(function (key) { | ||||||
|  |           // don't confused devs with the id url
 | ||||||
|  |           auth[key] = challenge[key]; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // batteries-included helpers
 | ||||||
|  |         auth.hostname = auth.identifier.value; | ||||||
|  |         // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
 | ||||||
|  |         auth.altname = ACME._untame(auth.identifier.value, auth.wildcard); | ||||||
|  | 
 | ||||||
|  |         auth.thumbprint = thumb; | ||||||
|  |         //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
 | ||||||
|  |         auth.keyAuthorization = challenge.token + '.' + auth.thumbprint; | ||||||
|  | 
 | ||||||
|  |         if ('http-01' === auth.type) { | ||||||
|  |           // conflicts with ACME challenge id url is already in use, so we call this challengeUrl instead
 | ||||||
|  |           // TODO auth.http01Url ?
 | ||||||
|  |           auth.challengeUrl = 'http://' + auth.identifier.value + ACME.challengePrefixes['http-01'] + '/' + auth.token; | ||||||
|  |           return auth; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ('dns-01' !== auth.type) { | ||||||
|  |           return auth; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return Crypto._sha('sha256', auth.keyAuthorization).then(function (hash) { | ||||||
|  |           auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', ''); | ||||||
|  |           auth.dnsAuthorization = hash; | ||||||
|  |           auth.keyAuthorizationDigest = hash; | ||||||
|  |           return auth; | ||||||
|  |         }); | ||||||
|  |       })).then(function (auths) { | ||||||
|  |         return auths.filter(Boolean); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @ -2241,18 +2236,21 @@ ACME._postChallenge = function (me, options, auth) { | |||||||
|         return resp.body; |         return resp.body; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var errmsg; |       var err; | ||||||
|       if (!resp.body.status) { |       if (resp.body.error && resp.body.error.detail) { | ||||||
|         errmsg = "[acme-v2] (E_STATE_EMPTY) empty challenge state for '" + altname + "':"; |         err = new Error("[acme-v2] " + auth.altname + " state:" + resp.body.status + " " + resp.body.error.detail); | ||||||
|       } |         err.auth = auth; | ||||||
|       else if ('invalid' === resp.body.status) { |         err.altname = auth.altname; | ||||||
|         errmsg = "[acme-v2] (E_STATE_INVALID) challenge state for '" + altname + "': '" + resp.body.status + "'"; |         err.type = auth.type; | ||||||
|       } |         err.urn = resp.body.error.type; | ||||||
|       else { |         err.code = ('invalid' === resp.body.status) ? 'E_CHALLENGE_INVALID' : 'E_CHALLENGE_UNKNOWN'; | ||||||
|         errmsg = "[acme-v2] (E_STATE_UKN) challenge state for '" + altname + "': '" + resp.body.status + "'"; |         err.uri = resp.body.url; | ||||||
|  |       } else { | ||||||
|  |         err = new Error("[acme-v2] " + auth.altname + " (E_STATE_UKN): " + JSON.stringify(resp.body, null, 2)); | ||||||
|  |         err.code = 'E_CHALLENGE_UNKNOWN'; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return Promise.reject(new Error(errmsg)); |       return Promise.reject(err); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -2312,34 +2310,32 @@ ACME._setChallenge = function (me, options, auth) { | |||||||
| ACME._setChallengesAll = function (me, options) { | ACME._setChallengesAll = function (me, options) { | ||||||
|   var order = options.order; |   var order = options.order; | ||||||
|   var setAuths = order.authorizations.slice(0); |   var setAuths = order.authorizations.slice(0); | ||||||
|   var challenges = order.challenges; |   var claims = order.claims.slice(0); | ||||||
|   var validAuths = []; |   var validAuths = []; | ||||||
|   var auths = []; |   var auths = []; | ||||||
| 
 | 
 | ||||||
|   function setNext() { |   function setNext() { | ||||||
|     var authUrl = setAuths.shift(); |     var authUrl = setAuths.shift(); | ||||||
|     var results = challenges.shift(); |     var claim = claims.shift(); | ||||||
|     if (!authUrl) { return; } |     if (!authUrl) { return; } | ||||||
| 
 | 
 | ||||||
|     // var domain = options.domains[i]; // results.identifier.value
 |     // var domain = options.domains[i]; // claim.identifier.value
 | ||||||
| 
 | 
 | ||||||
|     // If it's already valid, we're golden it regardless
 |     // If it's already valid, we're golden it regardless
 | ||||||
|     if (results.challenges.some(function (ch) { return 'valid' === ch.status; })) { |     if (claim.challenges.some(function (ch) { return 'valid' === ch.status; })) { | ||||||
|       return setNext(); |       return setNext(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var challenge = ACME._chooseChallenge(options, results); |     var auth = ACME._chooseAuth(options, claim.challenges); | ||||||
|     if (!challenge) { |     if (!auth) { | ||||||
|       // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 |       // For example, wildcards require dns-01 and, if we don't have that, we have to bail
 | ||||||
|       return Promise.reject(new Error( |       return Promise.reject(new Error( | ||||||
|         "Server didn't offer any challenge we can handle for '" + options.domains.join() + "'." |         "Server didn't offer any challenge we can handle for '" + options.domains.join() + "'." | ||||||
|       )); |       )); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ACME._challengeToAuth(me, options, results, challenge, false).then(function (auth) { |     auths.push(auth); | ||||||
|       auths.push(auth); |     return ACME._setChallenge(me, options, auth).then(setNext); | ||||||
|       return ACME._setChallenge(me, options, auth).then(setNext); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function checkNext() { |   function checkNext() { | ||||||
| @ -2358,6 +2354,7 @@ ACME._setChallengesAll = function (me, options) { | |||||||
|     }).then(checkNext); |     }).then(checkNext); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // Actually sets the challenge via ACME
 | ||||||
|   function challengeNext() { |   function challengeNext() { | ||||||
|     var auth = validAuths.shift(); |     var auth = validAuths.shift(); | ||||||
|     if (!auth) { return; } |     if (!auth) { return; } | ||||||
| @ -2519,7 +2516,7 @@ ACME._createOrder = function (me, options) { | |||||||
|       if (me.debug) { console.debug('[ordered]', location); } // the account id url
 |       if (me.debug) { console.debug('[ordered]', location); } // the account id url
 | ||||||
|       if (me.debug) { console.debug(resp); } |       if (me.debug) { console.debug(resp); } | ||||||
| 
 | 
 | ||||||
|       if (!options.authorizations) { |       if (!order.authorizations) { | ||||||
|         return Promise.reject(new Error( |         return Promise.reject(new Error( | ||||||
|           "[acme-v2.js] authorizations were not fetched for '" + options.domains.join() + "':\n" |           "[acme-v2.js] authorizations were not fetched for '" + options.domains.join() + "':\n" | ||||||
|           + JSON.stringify(resp.body) |           + JSON.stringify(resp.body) | ||||||
| @ -2528,25 +2525,24 @@ ACME._createOrder = function (me, options) { | |||||||
| 
 | 
 | ||||||
|       return order; |       return order; | ||||||
|     }).then(function (order) { |     }).then(function (order) { | ||||||
|       var challenges = []; |       var claims = []; | ||||||
|       if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); } |       if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); } | ||||||
|       var challengeAuths = order.authorizations.slice(0); |       var challengeAuths = order.authorizations.slice(0); | ||||||
| 
 | 
 | ||||||
|       function getNext() { |       function getNext() { | ||||||
|         var authUrl = challengeAuths.shift(); |         var authUrl = challengeAuths.shift(); | ||||||
|         if (!authUrl) { return challenges; } |         if (!authUrl) { return claims; } | ||||||
| 
 | 
 | ||||||
|         return ACME._getChallenges(me, options, authUrl).then(function (results) { |         return ACME._getChallenges(me, options, authUrl).then(function (claim) { | ||||||
|           // var domain = options.domains[i]; // results.identifier.value
 |           // var domain = options.domains[i]; // claim.identifier.value
 | ||||||
|           challenges.push(results); |           claims.push(claim); | ||||||
|           return getNext(); |           return getNext(); | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return getNext().then(function () { |       return getNext().then(function () { | ||||||
|         order.challenges = challenges; |         order.claims = claims; | ||||||
|         options.order = order; |         options.order = order; | ||||||
|         console.log('DEBUG 2 order (too much info for challenges?):', order); |  | ||||||
|         return order; |         return order; | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| @ -2684,10 +2680,12 @@ ACME.create = function create(me) { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|   me.orders = { |   me.orders = { | ||||||
|     create: function (options) { |     // create + get challlenges
 | ||||||
|  |     request: function (options) { | ||||||
|       return ACME._createOrder(me, options); |       return ACME._createOrder(me, options); | ||||||
|     } |     } | ||||||
|   , finalize: function (options) { |     // set challenges, check challenges, finalize order, return order
 | ||||||
|  |   , complete: function (options) { | ||||||
|       return ACME._finalizeOrder(me, options); |       return ACME._finalizeOrder(me, options); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
|   /*global URLSearchParams,Headers*/ |   /*global URLSearchParams,Headers*/ | ||||||
|  |   var PromiseA = window.Promise; | ||||||
|   var VERSION = '2'; |   var VERSION = '2'; | ||||||
| 	// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
 | 	// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
 | ||||||
| 	// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
 | 	// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
 | ||||||
| @ -15,11 +16,32 @@ | |||||||
|   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 acme; | 	var acme; | ||||||
| 	var accountStuff; |  | ||||||
|   var info = {}; |   var info = {}; | ||||||
|   var steps = {}; |   var steps = {}; | ||||||
|   var i = 1; |   var i = 1; | ||||||
|   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; |   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; | ||||||
|  |   var challenges = { | ||||||
|  |     'http-01': { | ||||||
|  |       set: function (auth) { | ||||||
|  |         console.log('Chose http-01 for', auth.altname, auth); | ||||||
|  |         return Promise.resolve(); | ||||||
|  |       } | ||||||
|  |     , remove: function (auth) { | ||||||
|  |         console.log('Can remove http-01 for', auth.altname, auth); | ||||||
|  |         return Promise.resolve(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   , 'dns-01': { | ||||||
|  |       set: function (auth) { | ||||||
|  |         console.log('Chose dns-01 for', auth.altname, auth); | ||||||
|  |         return Promise.resolve(); | ||||||
|  |       } | ||||||
|  |     , remove: function (auth) { | ||||||
|  |         console.log('Can remove dns-01 for', auth.altname, auth); | ||||||
|  |         return Promise.resolve(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   function updateApiType() { |   function updateApiType() { | ||||||
|     console.log("type updated"); |     console.log("type updated"); | ||||||
| @ -59,8 +81,29 @@ | |||||||
|     i += 1; |     i += 1; | ||||||
| 
 | 
 | ||||||
|     return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) { |     return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) { | ||||||
|  |       var ourfault = true; | ||||||
|       console.error(err); |       console.error(err); | ||||||
|       window.alert("Something went wrong. It's our fault not yours. Please email aj@rootprojects.org and let him know that 'step " + j + "' failed."); |       console.error(Object.keys(err)); | ||||||
|  |       if ('E_CHALLENGE_INVALID' === err.code) { | ||||||
|  |         if ('dns-01' === err.type) { | ||||||
|  |           ourfault = false; | ||||||
|  |           window.alert("It looks like the DNS record you set for " | ||||||
|  |             + err.altname + " was incorrect or did not propagate. " | ||||||
|  |             + "The error message was '" + err.message + "'"); | ||||||
|  |         } else if ('http-01' === err.type) { | ||||||
|  |           ourfault = false; | ||||||
|  |           window.alert("It looks like the file you uploaded for " | ||||||
|  |             + err.altname + " was incorrect or could not be downloaded. " | ||||||
|  |             + "The error message was '" + err.message + "'"); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (ourfault) { | ||||||
|  |         err.auth = undefined; | ||||||
|  |         window.alert("Something went wrong. It's probably our fault, not yours." | ||||||
|  |           + " Please email aj@rootprojects.org to let him know. The error message is: \n" | ||||||
|  |           + JSON.stringify(err, null, 2)); | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -178,7 +221,8 @@ | |||||||
|     $qs('.js-acme-form-domains').hidden = false; |     $qs('.js-acme-form-domains').hidden = false; | ||||||
|   }; |   }; | ||||||
|   steps[1].submit = function () { |   steps[1].submit = function () { | ||||||
|     info.identifiers = $qs('.js-acme-domains').value.split(/\s*,\s*/g).map(function (hostname) { |     info.domains = $qs('.js-acme-domains').value.replace(/https?:\/\//g, ' ').replace(/,/g, ' ').trim().split(/\s+/g); | ||||||
|  |     info.identifiers = info.domains.map(function (hostname) { | ||||||
|       return { type: 'dns', value: hostname.toLowerCase().trim() }; |       return { type: 'dns', value: hostname.toLowerCase().trim() }; | ||||||
|     }).slice(0,1); //Disable multiple values for now.  We'll just take the first and work with it.
 |     }).slice(0,1); //Disable multiple values for now.  We'll just take the first and work with it.
 | ||||||
|     info.identifiers.sort(function (a, b) { |     info.identifiers.sort(function (a, b) { | ||||||
| @ -204,12 +248,14 @@ | |||||||
|   steps[2].submit = function () { |   steps[2].submit = function () { | ||||||
|     var email = $qs('.js-acme-account-email').value.toLowerCase().trim(); |     var email = $qs('.js-acme-account-email').value.toLowerCase().trim(); | ||||||
| 
 | 
 | ||||||
|  |     info.email = email; | ||||||
|     info.contact = [ 'mailto:' + email ]; |     info.contact = [ 'mailto:' + email ]; | ||||||
|     info.agree = $qs('.js-acme-account-tos').checked; |     info.agree = $qs('.js-acme-account-tos').checked; | ||||||
|     //info.greenlockAgree = $qs('.js-gl-tos').checked;
 |     //info.greenlockAgree = $qs('.js-gl-tos').checked;
 | ||||||
|  |     info.domains = info.identifiers.map(function (ident) { return ident.value; }); | ||||||
| 
 | 
 | ||||||
|     // TODO ping with version and account creation
 |     // TODO ping with version and account creation
 | ||||||
|     setTimeout(saveContact, 100, email, info.identifiers.map(function (ident) { return ident.value; })); |     setTimeout(saveContact, 100, email, info.domains); | ||||||
| 
 | 
 | ||||||
| 		function checkTos(tos) { | 		function checkTos(tos) { | ||||||
| 			if (info.agree) { | 			if (info.agree) { | ||||||
| @ -227,10 +273,10 @@ | |||||||
| 			, accountKeypair: { privateKeyJwk: jwk } | 			, accountKeypair: { privateKeyJwk: jwk } | ||||||
| 			}).then(function (account) { | 			}).then(function (account) { | ||||||
| 				console.log("account created result:", account); | 				console.log("account created result:", account); | ||||||
| 				accountStuff.account = account; | 				info.account = account; | ||||||
| 				accountStuff.privateJwk = jwk; | 				info.privateJwk = jwk; | ||||||
| 				accountStuff.email = email; | 				info.email = email; | ||||||
| 				accountStuff.acme = acme; // TODO XXX remove
 | 				info.acme = acme; // TODO XXX remove
 | ||||||
| 			}).catch(function (err) { | 			}).catch(function (err) { | ||||||
| 				console.error("A bad thing happened:"); | 				console.error("A bad thing happened:"); | ||||||
| 				console.error(err); | 				console.error(err); | ||||||
| @ -241,116 +287,104 @@ | |||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		}).then(function () { | 		}).then(function () { | ||||||
|       var jwk = accountStuff.privateJwk; |       var jwk = info.privateJwk; | ||||||
|       var account = accountStuff.account; |       var account = info.account; | ||||||
| 
 | 
 | ||||||
| 			return acme.orders.create({ | 			return acme.orders.request({ | ||||||
| 			  account: account | 			  account: account | ||||||
| 			, accountKeypair: { privateKeyJwk: jwk } | 			, accountKeypair: { privateKeyJwk: jwk } | ||||||
| 			, identifiers: info.identifiers | 			, domains: info.domains | ||||||
|  |       , challenges: challenges | ||||||
| 			}).then(function (order) { | 			}).then(function (order) { | ||||||
| 				return acme.orders.create({ |         info.order = order; | ||||||
| 					signedOrder: signedOrder |  | ||||||
| 				}).then(function (order) { |  | ||||||
| 					accountStuff.order = order; |  | ||||||
|           var claims = order.challenges; |  | ||||||
|           console.log('claims:'); |  | ||||||
|           console.log(claims); |  | ||||||
| 
 | 
 | ||||||
|           var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] }; |         var claims = order.claims; | ||||||
|           info.challenges = obj; |         console.log('claims:'); | ||||||
|           var map = { |         console.log(claims); | ||||||
|             'http-01': '.js-acme-verification-http-01' |  | ||||||
|           , 'dns-01': '.js-acme-verification-dns-01' |  | ||||||
|           , 'wildcard': '.js-acme-verification-wildcard' |  | ||||||
|           }; |  | ||||||
|           options.challengePriority = [ 'http-01', 'dns-01' ]; |  | ||||||
| 
 | 
 | ||||||
|           // TODO make Promise-friendly
 |         var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] }; | ||||||
|           return PromiseA.all(claims.map(function (claim) { |         info.challenges = obj; | ||||||
|             var hostname = claim.identifier.value; |         /* | ||||||
|             return PromiseA.all(claim.challenges.map(function (c) { |         var map = { | ||||||
|               var keyAuth = BACME.challenges['http-01']({ |           'http-01': '.js-acme-verification-http-01' | ||||||
|                 token: c.token |         , 'dns-01': '.js-acme-verification-dns-01' | ||||||
|               , thumbprint: thumbprint |         , 'wildcard': '.js-acme-verification-wildcard' | ||||||
|               , challengeDomain: hostname |         }; | ||||||
|               }); |         */ | ||||||
|               return BACME.challenges['dns-01']({ |  | ||||||
|                 keyAuth: keyAuth.value |  | ||||||
|               , challengeDomain: hostname |  | ||||||
|               }).then(function (dnsAuth) { |  | ||||||
|                 var data = { |  | ||||||
|                   type: c.type |  | ||||||
|                 , hostname: hostname |  | ||||||
|                 , url: c.url |  | ||||||
|                 , token: c.token |  | ||||||
|                 , keyAuthorization: keyAuth |  | ||||||
|                 , httpPath: keyAuth.path |  | ||||||
|                 , httpAuth: keyAuth.value |  | ||||||
|                 , dnsType: dnsAuth.type |  | ||||||
|                 , dnsHost: dnsAuth.host |  | ||||||
|                 , dnsAnswer: dnsAuth.answer |  | ||||||
|                 }; |  | ||||||
| 
 | 
 | ||||||
|                 console.log(''); |         claims.forEach(function (claim) { | ||||||
|                 console.log('CHALLENGE'); |           console.log("Challenge (claim):"); | ||||||
|                 console.log(claim); |           console.log(claim); | ||||||
|                 console.log(c); |           var hostname = claim.identifier.value; | ||||||
|                 console.log(data); |           claim.challenges.forEach(function (c) { | ||||||
|                 console.log(''); |             var auth = c; | ||||||
|  |             var data = { | ||||||
|  |               type: c.type | ||||||
|  |             , hostname: hostname | ||||||
|  |             , url: c.url | ||||||
|  |             , token: c.token | ||||||
|  |             , httpPath: auth.challengeUrl | ||||||
|  |             , httpAuth: auth.keyAuthorization | ||||||
|  |             , dnsType: 'TXT' | ||||||
|  |             , dnsHost: auth.dnsHost | ||||||
|  |             , dnsAnswer: auth.keyAuthorizationDigest | ||||||
|  |             }; | ||||||
| 
 | 
 | ||||||
|                 if (claim.wildcard) { |             console.log(''); | ||||||
|                   obj.wildcard.push(data); |             console.log('CHALLENGE'); | ||||||
|                   let verification = $qs(".js-acme-verification-wildcard"); |             console.log(claim); | ||||||
|                   verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname; |             console.log(c); | ||||||
|                   verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; |             console.log(data); | ||||||
|                   verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; |             console.log(''); | ||||||
| 
 | 
 | ||||||
|                 } else if(obj[data.type]) { |             var verification; | ||||||
|  |             if (claim.wildcard) { | ||||||
|  |               obj.wildcard.push(data); | ||||||
|  |               verification = $qs(".js-acme-verification-wildcard"); | ||||||
|  |               verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname; | ||||||
|  |               verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; | ||||||
|  |               verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; | ||||||
| 
 | 
 | ||||||
|                   obj[data.type].push(data); |             } else if(obj[data.type]) { | ||||||
| 
 | 
 | ||||||
|                   if ('dns-01' === data.type) { |               obj[data.type].push(data); | ||||||
|                     let verification = $qs(".js-acme-verification-dns-01"); |  | ||||||
|                     verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname; |  | ||||||
|                     verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; |  | ||||||
|                     verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; |  | ||||||
|                   } else if ('http-01' === data.type) { |  | ||||||
|                     $qs(".js-acme-ver-file-location").innerHTML = data.httpPath.split("/").slice(-1); |  | ||||||
|                     $qs(".js-acme-ver-content").innerHTML = data.httpAuth; |  | ||||||
|                     $qs(".js-acme-ver-uri").innerHTML = data.httpPath; |  | ||||||
|                     $qs(".js-download-verify-link").href = |  | ||||||
|                       "data:text/octet-stream;base64," + window.btoa(data.httpAuth); |  | ||||||
|                     $qs(".js-download-verify-link").download = data.httpPath.split("/").slice(-1); |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
| 
 | 
 | ||||||
|               }); |               if ('dns-01' === data.type) { | ||||||
| 
 |                 verification = $qs(".js-acme-verification-dns-01"); | ||||||
|             })); |                 verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname; | ||||||
|           })).then(function () { |                 verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; | ||||||
| 
 |                 verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; | ||||||
|             // hide wildcard if no wildcard
 |               } else if ('http-01' === data.type) { | ||||||
|             // hide http-01 and dns-01 if only wildcard
 |                 $qs(".js-acme-ver-file-location").innerHTML = data.httpPath.split("/").slice(-1); | ||||||
|             if (!obj.wildcard.length) { |                 $qs(".js-acme-ver-content").innerHTML = data.httpAuth; | ||||||
|               $qs('.js-acme-wildcard-challenges').hidden = true; |                 $qs(".js-acme-ver-uri").innerHTML = data.httpPath; | ||||||
|  |                 $qs(".js-download-verify-link").href = | ||||||
|  |                   "data:text/octet-stream;base64," + window.btoa(data.httpAuth); | ||||||
|  |                 $qs(".js-download-verify-link").download = data.httpPath.split("/").slice(-1); | ||||||
|  |               } | ||||||
|             } |             } | ||||||
|             if (!obj['http-01'].length) { |  | ||||||
|               $qs('.js-acme-challenges').hidden = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             updateChallengeType(); |  | ||||||
| 
 |  | ||||||
|             console.log("MAGIC STEP NUMBER in 2 is:", i); |  | ||||||
|             steps[i](); |  | ||||||
|           }); |           }); | ||||||
| 
 |  | ||||||
|         }); |         }); | ||||||
|  | 
 | ||||||
|  |         // hide wildcard if no wildcard
 | ||||||
|  |         // hide http-01 and dns-01 if only wildcard
 | ||||||
|  |         if (!obj.wildcard.length) { | ||||||
|  |           $qs('.js-acme-wildcard-challenges').hidden = true; | ||||||
|  |         } | ||||||
|  |         if (!obj['http-01'].length) { | ||||||
|  |           $qs('.js-acme-challenges').hidden = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         updateChallengeType(); | ||||||
|  | 
 | ||||||
|  |         console.log("MAGIC STEP NUMBER in 2 is:", i); | ||||||
|  |         steps[i](); | ||||||
|       }); |       }); | ||||||
|     }).catch(function (err) { |     }).catch(function (err) { | ||||||
|       console.error('Step \'\' 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@rootprojects.org and let him know."); |       window.alert("An error happened (but it's not your fault)." | ||||||
|  |         + " Email aj@rootprojects.org to let him know that 'order and get challenges' failed."); | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -360,58 +394,44 @@ | |||||||
|     $qs('.js-acme-form-challenges').hidden = false; |     $qs('.js-acme-form-challenges').hidden = false; | ||||||
|   }; |   }; | ||||||
|   steps[3].submit = function () { |   steps[3].submit = function () { | ||||||
|     options.challengeTypes = [ 'dns-01' ]; |     var challengePriority = [ 'dns-01' ]; | ||||||
|     if ('http-01' === $qs('.js-acme-challenge-type:checked').value) { |     if ('http-01' === $qs('.js-acme-challenge-type:checked').value) { | ||||||
|       options.challengeTypes.unshift('http-01'); |       challengePriority.unshift('http-01'); | ||||||
|     } |     } | ||||||
|     console.log('primary challenge type is:', options.challengeTypes[0]); |     console.log('primary challenge type is:', challengePriority[0]); | ||||||
| 
 | 
 | ||||||
|     return getAccountKeypair(email).then(function (jwk) { |     steps[i](); | ||||||
|  |     return getAccountKeypair(info.email).then(function (jwk) { | ||||||
|       // for now just show the next page immediately (its a spinner)
 |       // for now just show the next page immediately (its a spinner)
 | ||||||
|       // TODO put a test challenge in the list
 |       // TODO put a test challenge in the list
 | ||||||
|       // TODO warn about wait-time if DNS
 |       // TODO warn about wait-time if DNS
 | ||||||
|       steps[i](); | 		  return getServerKeypair().then(function (serverJwk) { | ||||||
| 		  return getServerKeypair().then(function () { |         return acme.orders.complete({ | ||||||
|         return acme.orders.finalize({ |           account: info.account | ||||||
|           account: accountStuff.account |  | ||||||
|         , accountKeypair: { privateKeyJwk: jwk } |         , accountKeypair: { privateKeyJwk: jwk } | ||||||
|         , order: accountStuff.order |         , order: info.order | ||||||
|         , domainKeypair: 'TODO' |         , domains: info.domains | ||||||
|         }); |         , domainKeypair: { privateKeyJwk: serverJwk } | ||||||
|       }).then(function (certs) { |         , challengePriority: challengePriority | ||||||
|         console.log('WINNING!'); |         , challenges: challenges | ||||||
|         console.log(certs); |         }).then(function (certs) { | ||||||
|         $qs('#js-fullchain').innerHTML = certs; |           return Keypairs.export({ jwk: serverJwk }).then(function (keyPem) { | ||||||
|         $qs("#js-download-fullchain-link").href = |             console.log('WINNING!'); | ||||||
|           "data:text/octet-stream;base64," + window.btoa(certs); |             console.log(certs); | ||||||
|  |             $qs('#js-fullchain').innerHTML = [ | ||||||
|  |               certs.cert.trim() + "\n" | ||||||
|  |             , certs.chain + "\n" | ||||||
|  |             ].join("\n"); | ||||||
|  |             $qs("#js-download-fullchain-link").href = | ||||||
|  |               "data:text/octet-stream;base64," + window.btoa(certs); | ||||||
| 
 | 
 | ||||||
|         var wcOpts; |             $qs('#js-privkey').innerHTML = keyPem; | ||||||
|         var pemName; |             $qs("#js-download-privkey-link").href = | ||||||
|         if (/^R/.test(info.serverJwk.kty)) { |               "data:text/octet-stream;base64," + window.btoa(keyPem); | ||||||
|           pemName = 'RSA'; |             submitForm(); | ||||||
|           wcOpts = { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }; |           }); | ||||||
|         } else { |  | ||||||
|           pemName = 'EC'; |  | ||||||
|           wcOpts = { name: "ECDSA", namedCurve: "P-256" }; |  | ||||||
|         } |  | ||||||
|         return crypto.subtle.importKey( |  | ||||||
|           "jwk" |  | ||||||
|         , info.serverJwk |  | ||||||
|         , wcOpts |  | ||||||
|         , true |  | ||||||
|         , ["sign"] |  | ||||||
|         ).then(function (privateKey) { |  | ||||||
|           return window.crypto.subtle.exportKey("pkcs8", privateKey); |  | ||||||
|         }).then (function (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](); |  | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|     }).then(function () { |  | ||||||
|       return submitForm(); |  | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -424,11 +444,7 @@ | |||||||
|   steps[4].submit = function () { |   steps[4].submit = function () { | ||||||
|     console.log('Congrats! Auto advancing...'); |     console.log('Congrats! Auto advancing...'); | ||||||
| 
 | 
 | ||||||
| 
 |     window.alert("An error happened in the final step, but it's not your fault. Email aj@rootprojects.org and let him know."); | ||||||
|     }).catch(function (err) { |  | ||||||
|       console.error(err.toString()); |  | ||||||
|       window.alert("An error happened in the final step, but it's not your fault. Email aj@rootprojects.org and let him know."); |  | ||||||
|     }); |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   steps[5] = function () { |   steps[5] = function () { | ||||||
| @ -436,6 +452,8 @@ | |||||||
|     hideForms(); |     hideForms(); | ||||||
|     $qs('.js-acme-form-download').hidden = false; |     $qs('.js-acme-form-download').hidden = false; | ||||||
|   }; |   }; | ||||||
|  | 
 | ||||||
|  |   // The kickoff
 | ||||||
|   steps[1](); |   steps[1](); | ||||||
| 
 | 
 | ||||||
|   var params = new URLSearchParams(window.location.search); |   var params = new URLSearchParams(window.location.search); | ||||||
| @ -481,8 +499,8 @@ | |||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return testRsaSupport().then(function () { |     return testEcdsaSupport().then(function () { | ||||||
|       console.info('[crypto] RSA is supported'); |       console.info('[crypto] ECDSA is supported'); | ||||||
|     }).catch(function (err) { |     }).catch(function (err) { | ||||||
|       console.error('[crypto] could not use either RSA nor EC.'); |       console.error('[crypto] could not use either RSA nor EC.'); | ||||||
|       console.error(err); |       console.error(err); | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								index.html
									
									
									
									
									
								
							| @ -23,15 +23,10 @@ | |||||||
|     </style> |     </style> | ||||||
|     <link rel="preload" href="./app/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2" as="font" crossorigin="anonymous"> |     <link rel="preload" href="./app/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2" as="font" crossorigin="anonymous"> | ||||||
|     <link rel="preload" href="./app/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous"> |     <link rel="preload" href="./app/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous"> | ||||||
| 
 |  | ||||||
|     <link rel="prefetch" href="./app/fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2" as="font" crossorigin="anonymous"> |     <link rel="prefetch" href="./app/fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2" as="font" crossorigin="anonymous"> | ||||||
|     <link rel="prefetch" href="./app/js/app.js"> |     <link rel="prefetch" href="./app/js/bluecrypt-acme.js" as="script"> | ||||||
|     <link rel="prefetch" href="./app/js/bacme.js"> |     <link rel="prefetch" href="./app/js/greenlock.js" as="script"> | ||||||
|     <link rel="prefetch" href="./app/js/pkijs.org/v1.3.33/common.js"> |     <link rel="prefetch" href="./js/app.js" as="script"> | ||||||
|     <link rel="prefetch" href="./app/js/pkijs.org/v1.3.33/asn1.js"> |  | ||||||
|     <link rel="prefetch" href="./app/js/pkijs.org/v1.3.33/x509_schema.js"> |  | ||||||
|     <link rel="prefetch" href="./app/js/pkijs.org/v1.3.33/x509_simpl.js"> |  | ||||||
|     <link rel="prefetch" href="./app/js/browser-csr/v1.0.0-alpha/csr.js"> |  | ||||||
|   </head> |   </head> | ||||||
|   <body class="js-app-ready"> |   <body class="js-app-ready"> | ||||||
|     <script> |     <script> | ||||||
| @ -47,7 +42,8 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="column-row"> |       <div class="column-row"> | ||||||
|         <div class="js-javascript-warning"> |         <div class="js-javascript-warning"> | ||||||
|           Greenlock will process the CSR in the browser and request the certificates directly from letsencrypt.org.  Please enable Javascript before continuing. |           Greenlock will process the CSR in the browser and request the certificates directly from letsencrypt.org. | ||||||
|  |           Please enable Javascript before continuing. | ||||||
|         </div> |         </div> | ||||||
|         <form id="js-acme-form" action="./app/" method=> |         <form id="js-acme-form" action="./app/" method=> | ||||||
|           <div class="domain-psuedo-input"> |           <div class="domain-psuedo-input"> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user