寫這篇文章的起因是遇到了需要本機配置支持HTTPS協(xié)議的情況。
我們知道,因為HTTPS的安全性,越來越多的網(wǎng)絡應用支持HTTPS,比如GOOGLE優(yōu)先搜索支持HTTPS的網(wǎng)站;又比如小程序的網(wǎng)絡請求只能是HTTPS的。
于是就先回顧了一下OSI模型、HTTP和HTTPS協(xié)議,感興趣可以點擊下面鏈接參考筆者之前的文章:
圖解OSI七層模型
http協(xié)議
http協(xié)議之 8 種請求類型介紹
https協(xié)議
要支持HTTPS這邊就涉及獲取CA證書的問題,正式開發(fā)當然需要去購買,如果像筆者這樣只是開發(fā)內測,可以用自己生成的自簽名證書。生成數(shù)字證書的工具有openssl、keytool等。
openssl:SSL 密碼庫工具,其提供了一個通用、健壯、功能完備的工具套件,用以支持SSL/TLS 協(xié)議的實現(xiàn)。
keytool:JDK里面內置的一個數(shù)字證書生產(chǎn)工具,只能生成自簽名的數(shù)字證書。且不支持導出私鑰。所有的數(shù)字證書是以一條一條(采用別名區(qū)別)的形式存入證書庫的中,證書庫中的每個證書包含該條證書的私鑰,公鑰和對應的數(shù)字證書的信息。
一、CA的簽發(fā)過程
1)服務方 S 向第三方機構CA提交公鑰、組織信息、個人信息(域名)等信息并申請認證(申請證書不需要提供私鑰,確保私鑰永遠只能服務器掌握)
2)CA 通過線上、線下等多種手段驗證申請者提供信息的真實性,如組織是否存在、企業(yè)是否合法,是否擁有域名的所有權等
3)如信息審核通過,CA 會向申請者簽發(fā)認證文件-證書。
證書包含以下信息:申請者公鑰、申請者的組織信息和個人信息、簽發(fā)機構 CA 的信息、有效時間、證書序列號等信息的明文,同時包含一個簽名
簽名的產(chǎn)生算法:首先,使用散列函數(shù)計算公開的明文信息的信息摘要,然后,采用 CA 的私鑰對信息摘要進行加密,密文即簽名
4)客戶端 C 向服務器 S 發(fā)出請求時,S 返回證書文件
5)客戶端 C 讀取證書中的相關的明文信息,采用相同的散列函數(shù)計算得到信息摘要,然后,利用對應 CA 的公鑰解密簽名數(shù)據(jù),對比證書的信息摘要,如果一致,則可以確認 證書的合法性,即公鑰合法
6)客戶端然后驗證證書相關的域名信息、有效時間等信息
7)客戶端會內置信任 CA 的證書信息(包含公鑰),如果CA不被信任,則找不到對應 CA 的證書,證書也會被判定非法
在這個過程注意幾點:
1)申請證書不需要提供私鑰,確保私鑰永遠只能服務器掌握
2)證書的合法性仍然依賴于非對稱加密算法,證書主要是增加了服務器信息以及簽名
3)內置 CA 對應的證書稱為根證書,頒發(fā)者和使用者相同,自己為自己簽名(用CA自己的私鑰簽名),即自簽名證書(此證書中的公鑰即為CA的公鑰,可以使用這個公鑰對證書的簽名進行校驗,無需另外一份證書)
4)證書=公鑰+申請者與頒發(fā)者信息+簽名
公共鑰匙用來加密數(shù)據(jù),私有鑰匙用來計算簽名.
公鑰加密的消息只能用私鑰解密,私鑰簽名的消息只能用公鑰檢驗簽名。
二、https的通信過程
服務端需要認證的通信過程
- 客戶端發(fā)送請求到服務器端
- 服務器端返回證書和公開密鑰,公開密鑰作為證書的一部分而存在
- 客戶端驗證證書和公開密鑰的有效性,如果有效,則生成共享密鑰并使用公開密鑰加密發(fā)送到服務器端
- 服務器端使用私有密鑰解密數(shù)據(jù),并使用收到的共享密鑰加密數(shù)據(jù),發(fā)送到客戶端
- 客戶端使用共享密鑰解密數(shù)據(jù)
- SSL加密建立………
客戶端認證過程
客戶端需要認證的過程跟服務器端需要認證的過程基本相同,并且少了最開始的兩步。這種情況都是證書存儲在客戶端,并且應用場景比較少,一般金融才使用,比如支付寶、銀行客戶端都需要安裝證書。
三、keytool的使用
keytool是JDK自帶的密鑰和證書管理工具。它使用戶能夠管理自己的公鑰/私鑰對及相關證書,用于(通過數(shù)字簽名)自我認證(用戶向別的用戶/服務認證自己)或數(shù)據(jù)完整性以及認證服務。
密鑰庫中的條目類型只有兩種:密鑰項和可信任的證書項!
密鑰項- 每項存放極為敏感的加密密鑰信息,這種信息以一種受保護的格式儲存以防止未授權的訪問。通常,儲存在這類項中的密鑰是機密密鑰,或是伴有用于認證相應公鑰用的證書“鏈”的私鑰。keytool 只處理后一類型的項,即私鑰及其關聯(lián)的證書鏈。
可信任的證書項 - 每項包含一個屬于另一團體的公鑰證書。它之所以叫做“可信任的證書”,是因為密鑰倉庫的擁有者相信證書中的公鑰確實屬于證書“主體”(擁有者)識別的身份。證書簽發(fā)人通過對證書簽名來保證這點。
創(chuàng)建證書
//創(chuàng)建方式1:交互式;這種方式會一步步提示信息,按提示輸入相應信息即可
keytool -genkeypair -alias mytomcat -keyalg RSA -keysize 1024 -keypass 123456 -validity 365 -keystore d:\mykeystore.keystore -storepass 123456
//創(chuàng)建方式2:一步到位;把所需要的信息一步填寫完整,就不用分步操作了
keytool -genkeypair -alias "mytomcat" -keyalg "RSA" -keystore "d:\mykeystore.keystore" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass "123456" -storepass -validity 180
這邊對命令稍微解釋一下,keytool 的數(shù)據(jù)保存在密鑰庫里,訪問密鑰庫需要密碼,訪問密鑰庫中某個條目也是需要密碼,所以在創(chuàng)建密鑰庫、密鑰條目的時候,需要設定這兩種密碼,創(chuàng)建證書的過程中通過-storepass設定訪問密鑰庫的密碼,-keypass設定訪問密鑰庫中訪問某個條目的密碼。
在交互式證書創(chuàng)建過程中,“您的名字和姓氏”填寫自己應用的域名,具體域名根據(jù)自己實際需求修改,其他信息可以任意填寫。還有就是-alias后面的參數(shù)值(別名)在密鑰庫中是不區(qū)分大小寫的。
參數(shù)說明:
-genkeypair 表示要創(chuàng)建一個新的密鑰
-dname 表示密鑰的Distinguished Names, 表明了密鑰的發(fā)行者身份
CN=commonName 注:生成證書時,CN要和服務器的域名相同,如果在本地測試,就使用localhost
OU=organizationUnit
O=organizationName
L=localityName
S=stateName
C=country
-keyalg 使用加密的算法,這里是RSA
-alias 和keystore關聯(lián)的別名,這個alias通常不區(qū)分大小寫
-keypass 私有密鑰的密碼,這里設置為 123456
-keystore 密鑰保存在D:盤目錄下的mykeystore文件中
-storepass 存取密碼,這里設置為changeit,這個密碼提供系統(tǒng)從mykeystore文件中將信息取出
-validity 該密鑰的有效期為 180天 (默認為90天)
下面是各選項的缺省值。
-alias "mykey"
-keyalg "DSA"
-keysize 1024
-validity 90
-keystore 用戶宿主目錄中名為 .keystore 的文件
-file 讀時為標準輸入,寫時為標準輸出
附:cacerts證書文件是Java系統(tǒng)的CA證書倉庫,存在于java.home\jre\lib\security目錄下,這個倉庫的默認訪問密碼是
chageit;
使用命令可以查看Java系統(tǒng)的CA證書倉庫中所有證書:
keytool -list -keystore "yourPath/cacerts"
導出證書
通過上面的步驟生成了一個名為 mykeystore.keystore 的證書庫,以及存儲在里面的一條名為 mytomcat 的證書項;
下面導出證書:
keytool -export -alias mytomcat -keystore d:\mykeystore -file d:\mycerts.crt -storepass 123456
查看導出的證書信息
keytool -printcert -file mycerts.crt
把證書導入瀏覽器
在瀏覽器中導入這個證書,如果是購買的CA證書,就不需要導入,瀏覽器已內置了CA的根證書,在客戶端驗證證書時直接向CA請求驗證;我們自己生成的證書是沒有經(jīng)過CA認證的,所以只能自己手動導入
四、配置tomcat,使https訪問生效
1、redirectport改為443
為了使部署在tomcat中的應用可以提供https訪問能力,我們需要修改%tomcat_home%/conf/server.xml文件,將下面配置的redirectport改為443:
<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>
修改后:
<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="443"/>
這個redirectPort的作用是當客戶端以http方式訪問某個服務端資源,而這個資源又要求必須https訪問時,服務器重定向的端口號。這邊為什么需要將8443改為443呢?是因為443是https默認的端口,就像80是http默認的端口一樣,如果端口設置成443,那么在使用https訪問服務器的時候不需要帶端口,如果設置成8443,那么必須帶端口。
2、啟用SSL
將下面這行注釋去掉,啟用SSL,并且將端口從8443改為443,并且加上密鑰庫路徑以及密鑰庫訪問的密碼:
<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
增加兩個參數(shù),keystoreFile指定“密鑰庫”,keystorePass指定密鑰庫的密碼。
修改后:
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="d:/mytomcat.keystore" keystorePass="123456"/>
這樣并沒有完全配置好,還要注意protocol屬性值
<Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="d:/mytomcat.keystore" keystorePass="123456"/>
3、訪問http的時候自動跳轉到https
現(xiàn)在為了能在訪問http的時候自動跳轉到https,還需要配置%tomcat_home%/conf/web.xml,將下面這段代碼加入到節(jié)點:<welcome-file-list />后面:
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>Client Cert Users-only Area</realm-name>
</login-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
如果要取消http自動跳轉https,那么將這段刪除或者將CONFIDENTIAL改成NONE即可!
五、使用openssl生成證書
根據(jù)上面的步驟已經(jīng)可以正常部署項目,并通過https://localhost/yourdemo 訪問了。
但是,使用 keytool 生成的證書在 chrome58 之前沒有問題,在 chrome58 之后,會 COMMON_NAME 報錯:
Error: "Subject Alternative Name Missing"
or
NET::ERR_CERT_COMMON_NAME_INVALID
or
"Your connection is not private"
本地證書被拒絕的原因是,Chrome已經(jīng)不再支持證書中的commonName匹配,實際上,自2017年1月起就需要 subjectAltName 這個規(guī)則了。chrome58后 commonName 改成了SubjectAlternativeName 校驗域名;
所以要通過對已有證書添加 v3.ext 重新生成證書,在 v3.ext 里加入 subjectAltName
解決方案:使用 OpenSSL 生成所有的證書。
1、生成私鑰
openssl genrsa -des3 -out rootCA.key 2048密碼123456
生成證書申請文件(文件中只包含了公鑰和一些認證實體信息:用來做證書申請的)
openssl req -new -key rootCA.key -out rootCA.csr
生成根證書
openssl x509 -req -days 365 -sha256 -extfile d:\ssl\ssl.cnf -extensions v3_ca -signkey rootCA.key -in rootCA.csr -out rootCA.crt2、根證書pem
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem3、創(chuàng)建一個新的OpenSSL配置文件,server.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[dn]
C=CN
ST=shenzhen
L=shenzhen
O=shenzhen
OU=shenzhen
emailAddress=admin@localhost
CN=localhost
- 4、創(chuàng)建一個v3.ext文件,以創(chuàng)建一個X509 v3證書。注意我們指定了subjectAltName選項。
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
5、創(chuàng)建證書密鑰server.key;以存儲有 localhost 的配置 server.csr.cnf 進行設置 。
openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <(cat server.csr.cnf)6、證書簽名請求通過我們之前創(chuàng)建的根證書rootCA.pem頒發(fā),創(chuàng)建出一個localhost的域名證書。輸出證書文件server.crt。
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext7、將文件server.key和server.crt文件復制到服務器上可訪問的位置(具體操作可參考本文最后的總結部分)
以上7個步驟,1、2是生成自制根證書;3、4是配置文件;5、6是用配置文件和根證書生成服務端的證書;在實踐中發(fā)現(xiàn),不用制作根證書(即:省略1、2步),直接用配置文件生成服務端的證書也可以正常使用。
總結一下步驟:
只需4步即可生成證書:
- 生成私鑰 .key
openssl genrsa -out [name].key 2048 - 通過 .key 生成 .csr證書申請文件
openssl req -new -key [name].key -out [name].csr
檢查 & 打印 .csr
openssl req -text -noout -verify -in [name].csr - 通過 .key 和 .csr 生成 .crt證書
openssl x509 -req -days 365 -in [name].csr -signkey [name].key -out [name].crt
查看 .crt 內容
openssl x509 -in [name].crt -text -noout - 通過已有證書添加 v3.ext 重新生成證書
v3.ext文件
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
最后的 DNS.1 = localhost 也可以直接寫成 DNS = localhost。
openssl x509 -req -extfile v3.ext -days 3650 -in ssl.csr -CA ssl.crt -CAkey ssl.key -CAcreateserial -out ssl.crt
附1:不需要新建v3.ext文件的方法,直接把subjectAltName添加到證書里:
openssl x509 -req -extfile <(printf "subjectAltName=DNS:localhost") -days 365 -in ssl.csr -CA ssl.crt -CAkey ssl.key -CAcreateserial -out ssl.crt
附2:一步到位法:通過 .cnf 配置文件生成證書
上面的步驟是先生成證書,再用添加v3.ext文件的方法添加subjectAltName;下面直接用配置文件生成證書,一步到位:
(1)、ssl.cnf
[req]
prompt = no
default_bits = 4096
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req
[dn]
C=CN
ST=Shanghai
L=Shanghai
O=TEST
OU=Testing Domain
CN=localhost
emailAddress=admin@localhost
[v3_req]
keyUsage=keyEncipherment, dataEncipherment
extendedKeyUsage=serverAuth
subjectAltName=@alt_names
[alt_names]
DNS.1=localhost
(2)、通過 ssl.cnf 生成 .key、.crt
openssl req -new -newkey rsa:2048 -sha1 -days 180 -nodes -x509 -keyout myssl.key -out myssl.crt -config d:\ssl\ssl.cnf
(3)、通過 .cnf 文件生成 .csr
openssl req -new -config d:\ssl\ssl.cnf -key d:\ssl\myssl.key -out myssl.csr
(4)、查看生成的證書的命令:openssl x509 -in d:\ssl\myssl.crt -text -noout
openssl 總結:
openssl創(chuàng)建 .cnf 配置文件
openssl通過 .cnf 生成 .key、.crt
這兩步只是生成了證書,要想在項目中配置、使用他們,還要做下面兩步:
- 生成服務端p12文件
openssl pkcs12 -export -in d:\ssl\myssl.crt -inkey d:\ssl\myssl.key -out d:\ssl\myssl.p12 -name "server" - 把p12導入keystore
keytool -importkeystore -v -srckeystore d:\ssl\myssl.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore d:\ssl\mytest.keystore -deststoretype jks -deststorepass 123456 - tomcat里配置ssl,增加keystore
并注意把證書導入系統(tǒng),和瀏覽器中;
hosts 文件注意 loalhost 要對應127.0.0.1
臨時解決方案
如何讓 Chrome 信任自簽名證書:臨時方案
不檢查證書
chrome://flags/#allow-insecure-localhost