Try   HackMD

如何在 windows 上建立 SSL 憑證

tags: w3HexSchool . ssl

2020-12-15 更新

certbot 已支援 windows 上設定 ssl 詳情請見 官方網站

最近需要在 windows 上使用 https , 遇到 https 想當然要設定 SSL 憑證啦! 🧐
以前都馬在 Linux 上使用 certbot , 真是非常 happy 😆
今天要在 windows 上安裝 SSL 憑證 ,
結果發現 certbot 不支援 windows 😱 ,
讓我們來看看在 windows 上可以如何來處理這該死的 SSL 憑證吧 !😠

膜拜完 Google 大神後 , 發現有 3 種方式可行

  • 建立不可信任的憑證 , 我們可以使用 selfsigned 產生
  • 如果要一個可信任憑證的話 , 可用 zerossl 網站產生
  • 或是用 Crypt-LE 的 le64.exe 來產生
  • [2020-12-15 更新] 使用 certbot 產生 SSL 憑證

建立測試用的憑證 - selfsigned

安裝 selfsigned

npm i -s selfsigned

建立 generate.js 檔案 , 執行之

// generate.js const selfsigned = require('selfsigned'); const domain_names = 'localhost,sit.ttfb.com.tw'; // 不同的 domain_name 用 , 做區隔 const attrs = [{name: 'commonName', value: domain_names}]; const pems = selfsigned.generate(attrs, {days: 365}); console.log(pems); const fs = require('fs'); // 下方會將檔案存到與 js 同層的位置 fs.writeFileSync('./key.pem', pems.private, 'utf8'); fs.writeFileSync('./cert.pem', pems.cert, 'utf8'); fs.writeFileSync('./.fingerprint', pems.fingerprint, 'utf8'); fs.writeFileSync('./.public.pub', pems.public, 'utf8');

執行後會產生 4 個檔案

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

讓我們用 https 模組 , 幫我們確認 SSL cert 產生的狀況吧 !

// server.js const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('./key.pem'), cert: fs.readFileSync('./cert.pem') }; https.createServer(options, function (req, res) { res.end('secure!'); }).listen(443); // Redirect from http port 80 to https const http = require('http'); http.createServer(function (req, res) { console.log('req.headers=', req.headers); res.writeHead(301, {"Location": "https://" + req.headers['host'] + req.url}); res.end(); }).listen(80, () => console.log('http start well'));

然後用瀏覽器訪問 https://localhost 可看到以下畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

利用網頁服務建立可信任的憑證 - zerossl

Dashboard -> New Certificates

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

輸入 Domains -> Next Step
(網頁會自動做 nslookup 的檢查 , 所以要確保 nslookup 在 8.8.8.8 上查的到)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

使用 90 天的 SSL 憑證 -> Next Step

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

自動生成 CSR 檔 -> Next Step

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

使用免費的 ( Free ) -> Next Step

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

利用 Http File Upload 作網站所有權驗證
( 我們需要向網站 zerossl 證明 Domain 是我們的 , 它才能派發可信任的 SSL 憑證給我們 < 此機制可防止駭客仿冒憑證 > )

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

下載 Auth File , 771957028E5EC614768BB40D0AD8C09E.txt

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

使用 express.js 的 app.get 輸出 Auth File 的 txt 內容

// app.js const fs = require('fs'); const express = require('express'); const app = express(); const port = 80; const file_name = '771957028E5EC614768BB40D0AD8C09E.txt'; app.get(`/.well-known/pki-validation/${file_name}`, (req, res) => { const txt = fs.readFileSync(`./auth/${file_name}`).toString(); res.setHeader('content-type', 'text/plain'); res.send(txt); }); const server = app.listen(port, () => { console.log(`express.js start at http://localhost:${server.address().port}`); });

需要將 771957028E5EC614768BB40D0AD8C09E.txt 放入 auth 資料夾中

// 資料夾結構

----- 
  |---- app.js
  |----  auth
           |----- ???.txt

然後用瀏覽器查看 http://stg.thaitown.andrewdeveloper.com/.well-known/pki-validation/771957028E5EC614768BB40D0AD8C09E.txt 確認 txt 內容是否可訪問到 txt 檔的內容

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

如果可看到 Auth File 的內容 , 按下 Next Step , 接著去 Verify Domain 了!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

驗證成功後 , 會出現下方畫面 , 之後就可以下載 SSL cert 檔

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

選擇 nginx -> Download Certificate , 下載檔案

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

將下載的檔案 , 解壓縮到 cert 資料夾

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

建立 server.js 在 ssl 資料夾當作 http 與 https 的伺服器 , 用於驗證下載的 SSL 憑證是正常可用的受信任憑證

// server.js const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('./cert/private.key'), cert: fs.readFileSync('./cert/certificate.crt') }; https.createServer(options, function (req, res) { res.end('secure!'); }).listen(443); const http = require('http'); http.createServer(function (req, res) { // Redirect from http to https res.writeHead(301, {"Location": "https://" + req.headers['host'] + req.url}); res.end(); }).listen(80, () => console.log('http start well'));

然後用瀏覽器訪問 https://stg.thaitown.andrewdeveloper.com/

這樣我們就成功設定 SSL 憑證了 !
( P.S. 這個憑證只 3 個月有效 , 會失效的別忘了更新歐 )

利用 exe 檔建立可信任的憑證 - Crypt-LE

由於需要 public host_name 所以我們使用 aws 上的 windows 伺服器作測試

開啟 80 port 讓外部可以訪問

請參考 : https://stackoverflow.com/questions/56456926/aws-windows-server-hosting-ports-other-than-80-not-working

下載 le64.exe

下載位置 : https://github.com/do-know/Crypt-LE/releases

將下載的 le64.zip 解壓縮到桌面的 ssl 資料夾中 , 可看到檔案 le64.exe , 這就是我們之後要使用的工具

準備執行的 script

在 ssl 資料夾中 , 建立一個 add-ssl-script.txt 文字檔 , 並輸入下方文字於其中 ( 記得要改 [your_email] . [target_domain_name] 這兩個參數歐 ! )

le64.exe -email "[your_email]" -key account.key -csr domain.csr -csr-key domain.key -crt domain.crt -domains "[target_domain_name]" -generate-missing -live

將 add-ssl-script.txt 的副檔名改成 .bat , 然後執行它

add-ssl-script.bat 執行中 , 如果卡住的話 , 可按 enter 讓其執行下一動作

如果出現 Error requesting certificate 代表此次建立 cert 失敗 !

驗證的其中一個步驟會要求您將指定的 Url 放上 le64 隨機生成的文字

根據上方圖片將 url : http://[your_host]/.well-known/acme-challenge/VCxJsc-la448P9QmVB6TTHFBjwQ0O3NSPE2WNL8tPf4 回應特定文字 VCxJsc-la448P9QmVB6TTHFBjwQ0O3NSPE2WNL8tPf4.I7C2KmyCB_pAhWSyEve1U2ulMwJJ7zIAMDjFSFx34io

驗證成功後 , 可看到 The job is done, enjoy your certificate!

測試 https 是否有效 , 我們請出 node 的 http . https 模組來幫助我們驗證

const https = require('https'); // cert : https://github.com/do-know/Crypt-LE#for-windows const fs = require('fs'); const options = { key: fs.readFileSync('./account.key'), cert: fs.readFileSync('./domain.csr') }; https.createServer(options, function (req, res) { res.end('secure!'); }).listen(443); // Redirect from http port 80 to https const http = require('http'); http.createServer(function (req, res) { console.log('req.headers=', req.headers); res.writeHead(301, {"Location": "https://" + req.headers['host'] + req.url}); res.end(); }).listen(80, () => console.log('http start well')); // if using ssl cert with Crypt-LE you can use cmd below: // le64.exe -email "andrew7810262000@yahoo.com.tw" -key account.key -csr domain.csr -csr-key domain.key -crt domain.crt -domains "ec2-13-229-136-81.ap-southeast-1.compute.amazonaws.com" -generate-missing -live

參考資料