自簡書發佈的上篇《生成本地測試用https證書,支持通配符和多域名,初學OpenSSL》以來,本地測試用https用的妥妥的。javascript
線上一直用的騰訊雲的免費證書(每一個域名都要一個證書(滑稽),今天線上用的通配符證書也搞定了,實現了一個證書包含多個域名(多個泛域名)。html
2019-10-8 更新:添加對gethttpsforfree網頁中全部RSA簽名進行自動簽名,提供的自動簽名代碼在下面。java
今年(2018)年初Let’s Encrypt已開放了通配符證書的申請《Wildcard Certificates Coming January 2018》,目前只支持經過dns解析進行驗證。沒有通配符的證書時在內心感受用起來會很累,一直沒有去嘗試。之前AlphaSSL的通配符證書卻是能夠免費申請,assl.loovit.net都已經跳轉到別的地方了。如今能夠勇敢的去用Let’s Encrypt的通配符證書了。git
官方並無簡單易用的客戶端
申請環境:本地(線上不亂搞),win7+Win32OpenSSLgithub
Let’s Encrypt提供的客戶端列表包括開源實現《ACME Client Implementations》,電腦上能裝上的基本上都試了一下(捆綁生產環境的軟件不鳥),好幾個依賴高版本的.Net(勉爲其難的裝了一下),始終沒搞懂怎麼耍,入門到放棄。而後用C#調用了一個封裝好的接口試了一下,目測工做量也不小,入門到放棄。web
《Let’s Encrypt 使用吐槽》這裏面說的比較贊同的一點這纔是你真正須要的 acme-client ,只幹申請證書這一件事的腳本
。哈哈,其實發現申請證書只是簡單的調用幾個api.letsencrypt.org
RESTful接口,而且都含有響應頭Access-Control-Allow-Origin:*
,就不去研究他們的文檔了,由於翻了通常官網,並無發現哪裏有這種直接了當的文檔。api
Let’s Encrypt提供的網頁客戶端列表中的Get HTTPS for free
https://gethttpsforfree.com/就是直接使用的接口,這個網站聲稱This website is static, so it can be saved and loaded locally. Just right-click and "Save Page As.."!
查看代碼確實是一個靜態網站。其餘幾個網頁客戶端也稍微試了一下,有些要註冊還主動幫你管理證書(不鳥)。其實一個靜態網頁也可以實現證書的申請,還不用下載安裝各類環境,比較靠譜。瀏覽器
使用Get HTTPS for free申請證書
地址https://gethttpsforfree.com/ 這個頁面使用起來很方便,由於該提示的地方都有提示,按照提示作沒有壓力,順利申請到了兩個域名合在一塊兒的證書。app
主要要手動作的事情:dom
- 建立兩個祕鑰,一個帳戶祕鑰,一個域名證書祕鑰(2048位,這個網站寫4096位感受有點大,百度和Let’s Encrypt都是2048位)
- 建立證書申請請求
- 屢次進行文本內容簽名(用帳戶祕鑰)
*. 申請好的證書製做成pfx文件導入IIS (我用IIS是要這一步的)
這些步驟均可以用腳本去處理,點一下->複製粘貼,手動也不比自動差(其實也沒有便捷的全自動,dns驗證仍是要手動寫入TXT記錄)
使用到的腳本
下面這些文件所有是在同一個文件夾裏面: 1.建立祕鑰.cmd
,2.生成請求.ini
,2.生成請求.cmd
,3.簽名.cmd
,3.簽名.txt
,4.導出.cmd
文件名:1.建立祕鑰.cmd
@echo off set /p isSet=建立祕鑰會覆蓋現有祕鑰,肯定要建立嗎?(y) if not "%isSet%"=="y" goto end echo 建立帳戶祕鑰 openssl genrsa -out key.account.private 2048 openssl rsa -in key.account.private -pubout -out key.account.public echo 建立證書祕鑰 openssl genrsa -out key.domain.key 2048 :end echo 結束 pause
文件名:2.生成請求.ini
這個文件是OpenSSL安裝目錄中的配置文件,copy過來改一下名字就好了,而後在文件結尾添加下面內容,域名本身加(泛解析的要把一級域名加上,不加他們會不會自動加不知道,目測不會自動加)
[ ext ] subjectAltName = @dns [ dns ] DNS.1 = baidu1.com DNS.2 = *.baidu1.com DNS.3 = baidu2.com DNS.4 = *.baidu2.com
文件名:2.生成請求.cmd
@echo off set /p isSet=須要先配置域名列表"2.生成請求.ini",配置好了嗎?(y) if not "%isSet%"=="y" goto end echo 生成請求 openssl req -new -sha256 -key key.domain.key -out domain.csr -subj "/" -reqexts ext -config 2.生成請求.ini :end echo 結束 pause
文件名:3.簽名.cmd
@echo off set /p isSet=須要先寫入"3.簽名.txt",寫好了嗎?(y) if not "%isSet%"=="y" goto end openssl dgst -sha256 -hex -sign key.account.private 3.簽名.txt :end echo 結束 pause
文件名:3.簽名.txt
這個文件是一個空文件,到時候有須要簽名的數據複製進來運行一下簽名.cmd就能快速得到簽名。
文件名:4.導出.cmd
@echo off echo 導出pfx,請輸入密碼1 pause openssl pkcs12 -export -out domain.pfx -inkey key.domain.key -in domain.crt :end echo 結束 pause
申請製做過程
- 全部文件準備好後(尤爲是
2.生成請求.ini
把域名配置好),直接運行1.建立祕鑰.cmd
,而後直接運行2.生成請求.cmd
。 - 把生成的
key.account.public
填入網頁Account Public Key
中。 - 把生成的
domain.csr
填入網頁Certificate Signing Request
中。 - 根據網頁提示簽名請求,每次簽名的時候:把文本複製到
3.簽名.txt
(只要引號裏面的文本內容,其餘內容刪除),而後運行3.簽名.cmd
獲得簽名,把RSA-SHA256(3.簽名.txt)= XXXXX
複製到網頁對應位置。 - 根據提示設置域名dns TXT解析,並完成驗證。
- 把最後一步生成的證書內容所有複製到新建的文件
domain.crt
中,域名證書就獲取成功啦。 - 額外的生成IIS用的pfx文件,運行一下
4.導出.cmd
設置一下密碼就好了,我提示輸入1做爲密碼,是由於我懶(滑稽。
秀一波
自動簽名
我使用了 fszlin/certes 來編寫了一個C#客戶端用來申請多域名證書,但常常操做到最後這個庫總是拋出申請錯誤信息,無奈每次手動用gethttpsforfree都能一次性完成申請。因此我就準備丟棄certes,僅僅使用gethttpsforfree。
但gethttpsforfree每次簽名最繁瑣的就是RSA簽名部分,複製來複制去,因爲前段時間在個人H5錄音庫xiangyuecn/Recorder內實現了一個RSA簽名功能,所以直接拿過來給gethttpsforfree安裝上了自動簽名功能。
我給gethttpsforfree的github把這個功能提交了一個issue(他們家關閉了wiki),做者就來了一句SPAM。哈哈哈,本身好用就好,該作的我都作了,結果我才無論。https://github.com/diafygi/gethttpsforfree/issues/164 裏面有詳細的自動簽名介紹。
自動簽名操做步驟:
- 打開 https://gethttpsforfree.com/頁面。
- 複製下面的代碼到瀏覽器控制檯裏面執行。
- 按頁面中的正常流程操做,填寫好私鑰,中途全部簽名操做都會自動完成。
(function(){ var privateSign=function(txt){ //Signature try{ var key=document.querySelector(".privateSignKey").value.replace(/-+.+?PRIVATE.+?-+|\s/g,""); var byts=Base64.decode(key); var asn1=ASN1.decode(byts); if(asn1.sub.length<9){//PKCS#8 asn1=asn1.sub[2].sub[0]; }; var get=function(n){ var item=asn1.sub[n]; var start = item.stream.pos+item.header; var end = item.stream.pos+item.header+item.length; var hex = item.stream.hexDump(start, end); hex=hex.replace(/\s/g,""); hex=hex.replace(/^00/g,"");//type=02 INTEGER:Remove the highest bit-filled 0x00 return RSA.HexToB64(hex); }; var n=get(1); var e=get(2); var d=get(3); var rsa=RSA(n,e,d); var sign=rsa.sign(txt,"SHA256"); if(!rsa.verify(txt,sign,"SHA256")){ alert("private key seems to be wrong!"); return ""; }; return RSA.B64ToHex(sign); }catch(e){ console.error(e); alert("private key parse error!"); return ""; } }; var install=function(){ HTMLInputElement.prototype._setAttribute=HTMLInputElement.prototype.setAttribute; HTMLInputElement.prototype.setAttribute=function(k,v){ var This=this; This._setAttribute.apply(This,arguments); if(!(k=="placeholder"&&v==RESULT_PLACEHOLDER)){ return; }; var signExec=function(){ var find=This.parentNode.querySelectorAll("input"); var arr=[]; for(var i=0;i<find.length;i++){ if(find[i].value.indexOf("openssl")+1){ arr.push(find[i]); }; }; if(arr.length!=1){ alert("no input box for signature was found!"); return; }; This.value=privateSign(/"(.+)"/.exec(arr[0].value)[1]); }; setTimeout(signExec,100); //blur exec This._setAttribute(k,"blur retry autofill sign. "+v); if(!This.isBindSignExec){ This.isBindSignExec=true; This.addEventListener("blur",function(){ if(This.value==""){ signExec(); }; }); }; }; var div=document.createElement("div"); div.innerHTML=` <div class="field"> <div style="color:#0b1">Account Privete Key (RSA PKCS#1 or PKCS#8) (It's just for signature!):</div> <textarea class="privateSignKey" placeholder="-----BEGIN RSA PRIVATE KEY----- OR -----BEGIN PRIVATE KEY----- ..."></textarea> </div> `; validate_account.appendChild(div); }; var script=document.createElement("script"); script.src="https://xiangyuecn.github.io/Recorder/assets/ztest-rsa.js"; script.onload=function(){ setTimeout(function(){ install(); },1000);//emmm... doc was ready }; document.querySelector("head").appendChild(script); })();