轉載請註明文章來源:tlanyan.me/asymmetric-…
前幾日做支付對接時,被對方文件中的加密方式搞暈乎了一會。意識到證照加密方面的理解不夠深入,事後查閱參考資料補習一波。本文是根據期間的學習,以及長期以來的實踐做出的總結。
加密方式
密碼學是涉及數學、電子資訊、計算機等多學科的一門重要學科,是現代網際網路安全的基石,也是目前如火如荼的區塊鏈技術的安全保障。概括來說,加密方式可歸結如下:
一、 不可逆加密
資訊摘要(Message Digest)和安全雜湊(Secure Hash)演算法屬於此類,常見的演算法包括MD5、SHA1、PBKDF2、bcrypt等。此類演算法可將任意大小的原始資料變換成規定長度的輸出,即獲取內容的數字指紋,常用於校驗原始內容是否被篡改。這類演算法的主要特點是:
- 不可逆性。除非窮舉等手段,原則上不存在根據密文推斷出原文的演算法;
- 雪崩效應。對輸入資料敏感,原始內容的極小改動會造成輸出的大差異;
- 防碰撞性。原則上很難找到兩組相同的原文,經過加密後密文相同。
左耳朵耗子的“RSYNC的核心演算法”介紹了MD5演算法在rsync中的具體應用。MD5和SHA1已經被證實不安全(王小云教授在04年找到MD5迅速碰撞方法,谷歌在17年完成了SHA1的第一次碰撞),實踐中建議至少用SHA-256演算法,或採用對算力不敏感的scrypt、Argon2等演算法。
雜湊演算法的一個變種是HMAC(Hash-based Message Authentication Code)演算法,用於解決身份認證和防抵賴。HMAC演算法的輸入為一個金鑰(通訊雙方共享)、一種雜湊演算法(常為經典雜湊演算法)和原始資料,輸出的內容格式取決於所採用的雜湊演算法。由於只有通訊雙方知曉金鑰,簽名正確的情況下可確認資訊就是由對方發出。
二、 可逆加密
雜湊演算法的簽名可保證通訊中的資料不被篡改,可逆加密演算法是還原出明文的關鍵。可逆加密演算法可分成三類:
- 基於演算法的加密演算法,也被稱為古典加密演算法,如http認證中的base64,比特幣生成地址用的base58(公開的演算法也可稱作編碼方式)。這類演算法主要對原始內容進行置換和替換得到密文,安全性依賴於演算法是否外洩;
- 對稱加密演算法,加密和解密使用同一個金鑰。對稱加密演算法的出現標誌密碼學進入現代密碼學階段,密文的安全性從依賴於演算法轉向依賴於金鑰。常見的對稱加密演算法有DES、3DES、AES;
- 非對稱加密演算法,加密和解密使用不同的金鑰。非對稱加密演算法開創了密碼學的里程碑,解決了對稱加密過程中金鑰分發的安全問題,被認為現代密碼學最偉大的發明。常見的演算法有RSA、DH(Diffie-Hellman)、橢圓曲線演算法(Elliptic curve cryptography,ECC)。
非對稱演算法設計巧妙,但實際中要結合對稱加密使用。原因是某些演算法不能加解密(DH、DSA),或者效率太低(RSA),或者能處理的資料大小有限制(RSA)。而對稱加密演算法的有點是速度快,加密強度高。常用非對稱演算法獲得共享金鑰,之後用對稱加密處理資料。
本文的重點是非對稱加密及其衍生概念,下面逐一介紹。
公鑰、私鑰和證照
除演算法外,非對稱加密中另外兩個重要的概念是公鑰和私鑰。公鑰對外公開,任何人均可持有和使用;私鑰自行保管,其安全性是通訊安危的關鍵。例如OpenSSH客戶端預設會拒絕用許可權開放的私鑰連線伺服器,會出現如下提示:
# 放開私鑰許可權
chmod 644 ~/.ssh/id_ras
# 連線伺服器
ssh server
# openssh 客戶端出現如下報錯:
Permissions 0644 for '/home/tlanyan/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/home/tlanyan/.ssh/id_rsa": bad permissions
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
複製程式碼
私鑰和公鑰的作用一般分為兩種:
- 公鑰加密,私鑰解密,主要用於通訊;
- 私鑰簽名,公鑰驗證,主要用於簽名。
本次做支付對接時,對其演算法疑慮的地方是需要用到私鑰,按理要用對方的公鑰加密才對啊!後來意識到是用作資料簽名,用客戶端的私鑰是正確的。
理論上有了公鑰和金鑰,雙方就可以安全無礙的通訊,那常說的證照是怎麼回事?
證照,顧名思義,就是證明的檔案。例如瀏覽器和tlanyan.me
伺服器通訊,瀏覽器怎麼知道對方就是tlanyan.me
對應的伺服器呢?在不可信的網路下通訊,中立的第三方作用就顯現出來了。權威的第三方中立機構(同常是Certificate Authority, CA)給tlanyan.me
的持有者頒發證照,證明其就是域名所有人。伺服器收到請求後將證照一起傳送出去,瀏覽器對證照進行檢查,並向第三方詢問是否為真,確認無誤後,就可以放心的通訊了。
證照包含公鑰,所以拿到證照意味著就拿到了對方的公鑰。幾乎所有的瀏覽器都會都證照進行校驗,以確保網頁通訊中的安全。使用自簽發的證照,或者過期、與請求主機不符合的證照,都會導致瀏覽器發出安全警告,提醒使用者潛在的風險。
CURL等第三方庫一般不會對證照進行檢查,伺服器互動時如何確保通訊的對方是真李逵而非李鬼?
答案是客戶端預先存一份伺服器證照,通訊時校驗服務端發來的證照與本地的是否一致。如果不一致,則說明遇到了中間人攻擊,或預設的通訊方實體已經變更。之前做微信支付的對接,不理解微信的伺服器證照的作用,後來才理解其深意。
許多國外網站使用https,照樣倒在國內偉大的防火牆之下。根據https加密通訊的特點,同時CA加持,原則上牆是不可能知道通訊的內容。那麼在https通訊時,牆是怎麼識別出來並阻斷的?個人認為有三個可切入的點:
- DNS汙染,返回錯誤的IP地址;
- 直接把域名的所有IP封掉;
- 根據HTTPS的互動流程,客戶端和伺服器協商金鑰階段的資料均為明文,獲取金鑰後才會加密資料(包括URL)。協商階段的證照必然出現網站主機名,防火牆在這個階段可識別進並阻斷。
以上想法出自個人猜測。
總結:通訊的私鑰應該總是被妥善保管,在不可靠的網路環境下通訊,證照能避免中間人攻擊。
CSR、PEM、keystore等
蘋果開發會接觸到CSR、證照,安卓開發會用到keystore,web開發會用到pem、金鑰、證照、jks等。這些都是什麼?
CSR(Certificate Sign Request)、公鑰、金鑰和證照歸屬為一類。CSR用來獲取證照,包含申請人的公鑰、郵件等證明身份的資訊。證照頒發結構(可以是自己)收到CSR後簽發證照,生成的證照中包含公鑰、有效期、持有人等資訊。私鑰可單獨生成,也可在生成CSR的同時生成。整個過程中,私鑰應當都要被妥善保管,不能洩露。
keystore、pem、cer/crt、key等檔案儲存格式可歸為一類。Java KeyStore(檔案字尾.keystore或.jks)是Java常用的儲存金鑰和證照的檔案格式,需要設定檔案密碼、別名和別名密碼,安卓打包和部署Tomcat時會用到;PEM(Privacy Enhanced Mail)以文字形式存放私鑰和證照(鏈);cer/crt和key分別用來存放證照和金鑰;另外一種常見的格式是pfx或者p12,同jks格式,這類檔案一般是二進位制,訪問需要密碼。
PKI(Public key infrastructure)體系構建在公鑰加密基礎之上,主要解決證照的頒發和管理問題。證照管理中應用廣泛的兩個標準是X509和PKCS。遵循X509標準的證照檔案結尾多為pem、der、crt等;遵循PKCS標準的證照常用字尾名是pfx,p12等。
本次對接暈乎的第二個地方是一處地方讀取金鑰需要密碼,另一處直接讀取。根據儲存格式可知原因:從遵循PKCS#12標準的pfx檔案讀取需要密碼,遵循X509規範的PEM檔案則可直接檢視金鑰內容。
OpenSSL
OpenSSL是通用的加密庫,openssl是基於其的命令列工具,上文提到的內容基本都在其功能範圍內。另一個與openssl類似的工具是GPG(GNU Privacy Guard),區別是OpenSSL遵循X509標準,GPG遵循OpenPGP標準。兩者加密的檔案在格式上有所差異,無法解開對方加密過的檔案。OpenSSL和GPG內建在大多數*nix系統中,可直接使用。以下的示例基於OpenSSL,gpg的用法可檢視文中最後的參考文獻。
openssl命令的man頁面描述了其能力範圍:
The openssl program is a command line tool for using the various cryptography functions of
OpenSSL's crypto library from the shell. It can be used for
o Creation and management of private keys, public keys and parameters
o Public key cryptographic operations
o Creation of X.509 certificates, CSRs and CRLs
o Calculation of Message Digests
o Encryption and Decryption with Ciphers
o SSL/TLS Client and Server Tests
o Handling of S/MIME signed or encrypted mail
o Time Stamp requests, generation and verification
複製程式碼
接下來看一些簡單的openssl使用示例:
md5:
echo tlanyan | openssl md5
## 結果與下條命令相同
echo tlanyan | md5sum
複製程式碼
aes加解密:
# 用法
# openssl aes-128-cbc -e -in 加密檔案 -out 解密檔案 -pass pass:密碼
# 例如
echo tlanyan > input
openssl aes-128-cbc -e -in input -out output -pass pass:1234567890abcdef
# 加密的內容在output中
# 解密
openssl aes-128-cbc -d -in output -o origin -pass pass:1234567890abcdef
複製程式碼
生成CSR、簽發證照:
# 先生成csr和私鑰
# 注意使用-nodes選項,否則私鑰會有密碼,用在nginx啟動時需要手動輸入
openssl req -new -out tlanyan.csr -newkey rsa:2048 -nodes -keyout tlanyan.priv.key
# 接下來的互動裡填入一些基本資訊,完畢後會生成tlanyan.csr和tlanyan.priv.key兩個檔案
# csr的格式如下:
# -----BEGIN CERTIFICATE REQUEST-----
# xxxx
# -----END CERTIFICATE REQUEST-----
# 金鑰檔案的格式類似
# 有了csr,接下來為自己簽發證照
openssl req -x509 -sha256 -nodes -days 365 -in tlanyan.csr -key tlanyan.priv.key -out tlanyan.crt
# 命令結束後,目錄中出現tlanyan.crt的證照檔案
# 校驗金鑰
openssl rsa -in tlanyan.priv.key --check
# 校驗csr
openssl req -in tlanyan.csr -verify
# 校驗證照
openssl x509 -in tlanyan.crt -text -noout
複製程式碼
轉換各種不同格式的證照:
# 將pem格式轉換成pfx/p12格式
openssl pkcs12 -export -out tlanyan.pfx -inkey tlanyan.priv.key -in tlanyan.crt
# 將pfx格式轉換成pem格式
openssl pkcs12 -in tlanyan.pfx -out tlanyan.cer -nodes
# 生成的tlanyan.cer檔案包含了證照和公鑰,對應匯入前的tlanyan.crt和tlanyan.priv.key兩個檔案
複製程式碼
pem和jks的格式轉換太過複雜,具體請看Oracle的文件。
以上演示了openssl工具包中的極小一部分命令。更多的用法請參考官方文件。
總結
本文介紹了非對稱加密和證照的相關概念,並演示了openssl命令的一些用法。文章涉及內容較多,理解上稍顯難度。另外本文參考了不少文章,理解上的不到之處敬請指正。
感謝閱讀!