非對稱加密與OpenSSL

隨着個人隱私越來越受重視, HTTPS也漸漸的流行起來, 甚至有許多網站都做到了全站HTTPS,
然而這種加密和信任機制也不斷遭遇挑戰,比如戴爾根證書攜帶私鑰,Xboxlive證書私鑰瀉露,
還有前一段時間的沃通錯誤頒發Github根域名SSL證書事件. 因此本文從非對稱加密說起,
介紹了證書的簽證流程, 並且通過openssl的命令行工具對這些過程都轉化爲相對具體的命令,
也算是一個溫故知新的簡要記錄吧.

前言

一般來說,常見的數字加密方式都可以分爲兩類,即對稱加密和非對稱加密. 對於對稱加密來說,
加密和解密用的是同一個密鑰, 加密方法有AES,DES,RC4,BlowFish等; 對應的, 非對稱加密在
加密和解密時, 用的是不同的密鑰, 分別稱爲公鑰或私鑰. 非對稱加密的加密方法有RSA, DSA,
Diffie-Hellman等.

OpenSSL是一個開源項目,爲傳輸層安全(TLS)和安全套接字(SSL)協議提供了比較完整的實現,
同時也致力於將自身打造爲一個通用的密碼學工具集. 其中包括:

  • libssl : 提供了SSL(包括SSLv3)和TLS的服務器端以及客戶端的實現.
  • libcrypto : 通用的密碼學庫以及對X.509的支持
  • openssl : 一個多功能的命令行工具

本文主要使用openssl的命令行工具來示例非對稱加密的流程, 如果有興趣的話,也可以用其SDK
來實現更具體的操作.

加解密過程

創建公私鑰對

首先用openssl生成私鑰:

openssl genrsa -out private.pem 1024 

當然爲了更加安全,可以在生成私鑰的時候同時指定密碼, 這樣即使不小心瀉露了私鑰,也能增加別人的盜用難度:
openssl genrsa -aes256 -passout stdin -out private.pem 1024
openssl genrsa -aes256 -passout file:passwd.txt  -out private.pem 1024
openssl genrsa -aes256 -passout pass:my_password -out private.pem 1024

其中 -passout 指定密碼的輸入方式,可以分別是stdin,從文件中讀取或者緊接着pass:後面輸入.
有了私鑰,便可以從其中提取出公鑰:

openssl rsa -in private.pem -pubout -out public.pem

用公私鑰進行加解密

在一次祕密的信息傳輸中, 我們首先通過可信的方式(比如面對面)將公鑰告知對方, 對方發送機密信息的時候
就可以用我們的公鑰加密:

openssl rsautl -encrypt -pubin -inkey public.pem -in file.txt -out file.txt.enc

在發送的過程中即便泄露了文件,也無法查看文件的明文信息. 而我們收到密文後, 用私鑰解密即可:

openssl rsautl -decrypt -inkey private.pem -in file.txt.enc -out file.txt.dec

和對稱加密協作

雖然公私鑰加密很好用, 但事實上非對稱加密的缺點是加解密速度要遠遠慢於對稱加密, 在某些極端情況下,
甚至能比非對稱加密慢上千倍. 另外由於RSA算法的工作機理, 如果密鑰是n比特的,那麼其加密的信息容量就
不能大於(n-11)比特. 因此對於大文件的加密傳輸, 通常還是使用對稱加密的方式, 例如

openssl rand -base64 128 -out aeskey.txt
openssl enc -aes-256-cbc -salt -in file.txt -out file.txt.aesenc -pass file:aeskey.txt
openssl enc -d -aes-256-cbc -in file.txt.aesenc -out file.txt.aesdec -pass file:aeskey.txt

其中aeskey.txt是我們隨機生成密碼文件, 並且用其可以對大文件進行對稱的加解密, 在實際中,
通常還會將密碼文件用公私鑰加密的方式來發送給對方. 值得一提的是,這也正是PGP的工作方式,
如下圖所示:

pgp

證書

對任一個體來說, 它都有公鑰,私鑰和證書. 其中私鑰用來加密發出去的信息,公鑰用來解密收到的信息,
而證書則用來證明自己的身份. 一般來說,證書中包含自己的公鑰以及額外的信息,如簽發機構(CA, Certificate Authority),
證書用途(比如適用的域名)和有效時間等. CA通常是個第三方的可信機構, 比如VeriSign, GeoTrust,
DigiCert和沃通等, 當然也可以是未知的主體, 比如說自己.

獲得一張證書的流程通常是: 1)用私鑰生成證書籤名請求(csr), 2)將csr文件發送給CA,待其驗證信息無誤後,
CA會用自己的私鑰對其進行簽名表示確認.

生成證書籤名請求

證書籤名請求(Certificate Signing Request)通常以.csr爲後綴, 包含了請求方的公鑰和主體的詳細信息,
如域名,公司名,國家,城市等信息, 其完整內容可以參考這裏. 使用openssl也能很方便地生成csr:

openssl req -new -key private.pem -out pppan.csr

默認會在stdin中根據提示交互地輸入主體信息,也可以通過 -config 選項來從文件中讀取.
生成完之後可以通過:

openssl req -in pppan.csr -noout -text

來查看csr文件中的詳細信息.

CA對csr文件進行簽名

當CA收到csr文件並且對請求方的域名,公司等內容校驗無誤後,便可以對csr請求進行確認(簽名),

openssl req -x509 -newkey rsa:4096 -nodes -keyout cakey.pem -out cacert.pem -outform PEM
openssl ca -config openssl-ca.cnf -policy SP -extensions SR -infiles pppan.csr -out pppan.crt

雖然這不是重點, 但也稍微解釋下這兩個命令的意思吧. 第一個命令是CA一開始創建私鑰和CA的證書,
第二個命令表示對csr文件進行簽名確認, 用-config指定自定義的配置文件, 如果不指定則默認爲/usr/lib/ssl/openssl.cnf,
SP和SR都是自定義於配置文件中的信息, 此外配置文件中還包括CA證書路徑和私鑰路徑,以及對req的默認校驗策略等,
有興趣的可以查看詳細解釋.

另外值得一提的是, 我們用自己的私鑰也可以生成證書, 並且也能用這個證書來對自己的csr進行簽名,
這通常稱爲自簽名(self-signed), 上面CA生成的證書cacert.pem就是自簽名的. 一般來說,
如果是自己隨便生成自簽名證書, 通常會被認爲是不可信的, 除非手動添加到對方的信任CA證書列表中.

查看和驗證證書

CA對csr進行簽名後, 我們就能得到對應的證書, 這裏是pppan.crt, 可以用openssl查看證書的詳細信息:

openssl x509 -noout -text -in pppan.crt

可以看到具體的簽發機構,簽發時間和證書的有效時間等信息.
可以用命令驗證證書是否有效:

openssl verify -CAfile Trusts.pem pppan.crt

其中Trusts.pem是一系列所信任的證書集合,其中也包括了上述CA的證書cacert.pem

其他

上面所有用到的證書及其組件,如公鑰,私鑰,csr等,其格式都是PEM的,這也是最常見的一種格式,
可以用文本便及其打開,通常是以-----BEGIN XXX------開頭, 以-----END XXX-----結束,
中間的部分則是實際密鑰的base64編碼, 其二進制表示也稱爲DER格式, 兩者可以用base64轉化,
因此都屬於x509實現的證書格式.

還有比較常見的證書格式,爲PKCS7PKCS12. 其中PKCS7是由JAVA使用的開放標準,並且也被
Windows所支持, 其內是不包含私鑰信息的; 而PKCS12則是一種非公開的標準,用來提供比PEM的
純文本格式更高的安全性, 這是Windows建議使用的格式, 其中可以包含私鑰信息.

不同格式的轉換如下所示.

PEM <-> DER:

openssl x509 -in bar.pem -outform der -out bar.der
openssl x509 -inform der -in foo.der -out foo.pem

PEM <-> PKCS7:

openssl crl2pkcs7 -nocrl -certfile foocert.pem -out foocert.p7b 
openssl pkcs7 -in foocert.p7b  -print_certs -out barcert.pem  

PEM <-> PKCS12:

openssl pkcs12 -inkey private.key -in foocert.pem -export -out foocert.pfx
openssl pkcs12 -in foocert.pfx -nodes -out barcert.pem 

後記

當今我們使用最多的https本質上就是在http協議的基礎上對傳輸內容進行了非對稱的加密,
當然實現過程多了很多複雜的交互, 感興趣的可以去查看SSL和TLS協議. 我想說的是,
這一切信任機制的基石是對於CA的信任, 如果說CA的私鑰瀉露,或者我們錯誤地信任了一個壞CA,
那麼https的隱私性也就不復存在了, 因爲其可能對無效的csr進行簽名, 從而使得https中間人攻擊
成爲現實. 據說早在兩年前偉大的防火牆就已經可以對https進行監聽,敏感詞識別和連接重置,
後來因爲某種原因才從大範圍應用轉爲只對特殊對象使用,不過那是後話了.

博客地址:

歡迎交流,文章轉載請註明出處.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章