本文從TLS安全傳輸層協(xié)議的簡(jiǎn)單流程、如何生成自簽名CA證書、自頒發(fā)服務(wù)器&客戶端證書、openfire服務(wù)器安全配置等方面去描述如何建立一個(gè)使用TLS加密的XMPP聊天通道。
這里的smack版本是V4.2.3,openfire服務(wù)器版本也是V4.2.3
TLS
關(guān)于TLS協(xié)議,我們可以在網(wǎng)上找到很多相關(guān)的文章,這里不對(duì)基礎(chǔ)概念作過(guò)多介紹。這邊會(huì)結(jié)合HTTPS對(duì)TLS的應(yīng)用和XMPP中的sasl標(biāo)簽和starttls去了解TLS。但是我們需要知道TLS1.0是基于SSL3.0的,現(xiàn)在的SSL協(xié)議已經(jīng)不建議使用了。最新的TLS協(xié)議是1.3版本,還在完善中,應(yīng)用最廣的是TLS1.2協(xié)議。
TLS并不是一個(gè)傳輸層協(xié)議,而是一個(gè)傳輸層安全協(xié)議。而HTTP是一個(gè)應(yīng)用層協(xié)議,TLS的作用是對(duì)HTTP傳輸?shù)臄?shù)據(jù)進(jìn)行加密解密處理,以此來(lái)保證HTTP傳輸數(shù)據(jù)的安全,因?yàn)镠TTP對(duì)于數(shù)據(jù)的傳輸是明文的。
這里對(duì)HTTPS連接做簡(jiǎn)單描述,其具體作用在TCP三次連接建立以后,由客戶端(這里是C&S模型)發(fā)出Client Hello的TLS連接建立請(qǐng)求,然后服務(wù)器會(huì)告訴客戶端:服務(wù)器支持什么加密算法,發(fā)給客戶端自己掏大價(jià)錢買來(lái)的簽名證書的內(nèi)容。
然后客戶端使用本地預(yù)裝或者人為加裝的CA根證書去驗(yàn)證服務(wù)器發(fā)來(lái)的簽名證書是否有效,如果安全有效那就bingo,建立TLS連接,否則斷開連接。
而XMPP協(xié)議,在不使用TLS的時(shí)候,是一個(gè)明文的傳輸?shù)膮f(xié)議。其中的SASL協(xié)議只是起到了對(duì)鑒權(quán)數(shù)據(jù)(賬號(hào)密碼,或者其他用來(lái)確定登錄資源的數(shù)據(jù))的保密作用,starttls標(biāo)簽才是建立TLS連接的關(guān)鍵。
客戶端根據(jù)openfire服務(wù)器hostName,domainName和端口建立的連接也是一個(gè)TCP連接。在完成3次握手以后,客戶端使用XMPP協(xié)議和openfire服務(wù)器進(jìn)行通訊,服務(wù)器會(huì)返回一個(gè)服務(wù)器支持的sasl機(jī)制列表和是否強(qiáng)制使用TLS連接的報(bào)文數(shù)據(jù),如下
<stream:features>
<starttls
xmlns="urn:ietf:params:xml:ns:xmpp-tls">
<required/>
</starttls>
<mechanisms
xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>SCRAM-SHA-1</mechanism>
</mechanisms>
</stream:features>
下面的這個(gè)標(biāo)簽內(nèi)容,表明服務(wù)器強(qiáng)制要求使用TLS連接,客戶端跟服務(wù)器建立的TLS連接可以是雙向或者單向的,具體配置在文章下面會(huì)講到。
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">
<required/>
</starttls>
下面這段報(bào)文,注明服務(wù)器支持的sasl鑒權(quán)機(jī)制
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>PLAIN</mechanism>
<mechanism>SCRAM-SHA-1</mechanism>
</mechanisms>
如果使用TLS連接,那SASL機(jī)制可以使用PLAIN,否則的話推薦使用SCRAM-SHA-1,這是關(guān)于SASL相關(guān)機(jī)制的文章。
TLS加密建立
因?yàn)樯鲜鰣?bào)文中的值是required,所以客戶端會(huì)繼續(xù)發(fā)送要求進(jìn)行TLS連接驗(yàn)證的報(bào)文
//客戶端請(qǐng)求建立TLS連接
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>
//建立成功
<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
至此之后,我們的XMPP連接內(nèi)容使用上了TLS傳輸層安全協(xié)議來(lái)保駕護(hù)航了(新版本的XMPP傳輸報(bào)文不是明文的,也有一定的監(jiān)聽難度)。
關(guān)于CA自簽名
了解了TLS之后,因?yàn)檫@篇文章是出于學(xué)習(xí)目的,所以我們?cè)賹W(xué)一下證書簽名等相關(guān)知識(shí)吧(貧窮)。
同樣,在網(wǎng)上有很多關(guān)于數(shù)字簽名的文章,這里同樣不多做闡述,直接講述如何做一個(gè)CA機(jī)構(gòu)大佬。操作系統(tǒng)Centos7,需要用到的軟件有openssl和jdk。
生成自簽名證書
首先,了解一下openssl的目錄結(jié)構(gòu)。
openssl安裝成功以后,其目錄在/etc/pki下,里面有個(gè)配置文章特別重要,/etc/pki/tls/openssl.cnf這個(gè)配置文件約定了很多屬性,下文提到的cakey.pem,cacert.pem等文件名都是/etc/pki/tls/openssl.cnf規(guī)定的,所以下列操作直接復(fù)制粘貼就好了。
在此之前,把[ policy_match ]下面的幾個(gè)屬性改成這樣,否則簽名會(huì)出錯(cuò)
# For the CA policy
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
然后依次進(jìn)行如下操作,請(qǐng)注意使用sudo權(quán)限操作。
# cd /etc/pki/CA
//生成一個(gè)長(zhǎng)度是2048的私鑰文件
# (umask 077; openssl genrsa -out private/cakey.pem 2048)
//生成一個(gè)自簽名證書,有效期是3650天,時(shí)長(zhǎng)可以自由更改,里面需要填寫很多信息
# opensslreq -new -x509 -key private/cakey.pem -out cacert.pem -days 3650
//生成存儲(chǔ)證書序列號(hào)的文件
# touch index.txt serial
//先自增1
# echo 01 > serial
自此,CA自簽名證書搞定了,接下來(lái)就要給自己頒發(fā)一個(gè)openfire服務(wù)器證書了
生成openfire服務(wù)器證書、自簽名、配置
在此之前,簡(jiǎn)單描述一下TLS中,非對(duì)稱算法、CA結(jié)構(gòu)和簽名證書的作用。
- 非對(duì)稱算法:因?yàn)閷?duì)稱算法傳遞密鑰的不安全性,有了非對(duì)稱算法。其特點(diǎn)就是有兩個(gè)密鑰,一個(gè)公鑰一個(gè)私鑰,私鑰加密的內(nèi)容只有公鑰能解密,公鑰加密的內(nèi)容也只有私鑰才能解密。
假如B和A通訊,B需要把數(shù)據(jù)傳輸給A。A有一對(duì)密鑰,A保管私鑰,把公鑰告訴B。那么B用A的公鑰加密數(shù)據(jù),然后把數(shù)據(jù)發(fā)給A,A用自己的私鑰解密數(shù)據(jù)就知道B傳輸?shù)臄?shù)據(jù)是什么了。在A的公鑰安全可信的前提下,就保證了B傳輸數(shù)據(jù)的保密性了,不會(huì)被攔截的hack所獲取。但是假如hack把A的公鑰替換成自己的公鑰,B加密的數(shù)據(jù)就會(huì)被hack攔截了,那么如何保證A的私鑰的真實(shí)性呢?
- CA:我們的操作系統(tǒng)里面都會(huì)預(yù)裝一些權(quán)威CA機(jī)構(gòu)的根證書,被這些CA機(jī)構(gòu)開光(花錢為自己機(jī)構(gòu)注買一個(gè)數(shù)字證書)過(guò)的通訊對(duì)方就是可信機(jī)構(gòu),會(huì)有一個(gè)數(shù)字證書。只要被系統(tǒng)中預(yù)裝的某個(gè)CA結(jié)構(gòu)根證書驗(yàn)證通過(guò)了,就證明了通訊對(duì)方提供的公鑰是安全的(所以客戶端被做過(guò)手腳就不安全了)。
當(dāng)A和B使用TLS通訊的時(shí)候,A花錢去名叫C的CA機(jī)構(gòu)獲得了數(shù)字證書,在通訊的時(shí)候傳輸給B。B通過(guò)內(nèi)置的CA機(jī)構(gòu)根證書驗(yàn)證,鑒定這是真的A。之后使用A的公鑰,或者使用A的公鑰加密過(guò)的對(duì)稱密鑰(節(jié)省資源消耗)去加密數(shù)據(jù),再發(fā)送給A。這樣就保證了A收到的B的數(shù)據(jù)是安全的,也保證了B傳輸?shù)臄?shù)據(jù)不被hack所監(jiān)聽更改。
- 簽名證書:申請(qǐng)一個(gè)簽名證書,需要生成一個(gè)非對(duì)稱密鑰對(duì),生成一個(gè)簽名請(qǐng)求,這個(gè)簽名請(qǐng)求包含了申請(qǐng)簽名證書的機(jī)構(gòu)信息、數(shù)字證書有效期、申請(qǐng)機(jī)構(gòu)的域名等信息。CA機(jī)構(gòu)對(duì)申請(qǐng)機(jī)構(gòu)的簽名請(qǐng)求進(jìn)行開光以后,生成一個(gè)數(shù)字證書。這個(gè)數(shù)字證書就是用來(lái)證明申請(qǐng)機(jī)構(gòu)安全的保證。數(shù)字證書中包含了申請(qǐng)機(jī)構(gòu)的公鑰和信息的數(shù)字簽名。
openfire證書生成步驟
openfire服務(wù)器證書被安裝在/opt/openfire/resource/security/keystore中(這里的openfire安裝在CentOS_7系統(tǒng)上),我們需要在openfire網(wǎng)站中的服務(wù)器標(biāo)簽-TLS/SSL證書中,刪除自帶兩個(gè)證書。系統(tǒng)會(huì)自動(dòng)的在keystore文件中刪除這兩個(gè)證書,之后keystore就是一個(gè)空的密鑰容器了。接下來(lái)我們要使用openssl還有keytool生成還有轉(zhuǎn)換openfire服務(wù)器的證書了
我把openfire服務(wù)器放置在/home/keys/openfire目錄中,同樣按照以下操作敲命令即可,注意敲那些前面帶有#的
//創(chuàng)建`/home/keys/openfire目錄,進(jìn)入該目錄
//生成openfire證書的密鑰對(duì),接下來(lái)有個(gè)輸入openfire_key.pem密碼的操作,謹(jǐn)記密碼
# openssl genrsa -des3 -out openfire_key.pem 4096
//生成openfire_key.pem的簽名請(qǐng)求文件,按提示輸入密碼,其中需要注意Common Name是openfire的domain字符串,并不是openfire的hostName,謹(jǐn)記
# openssl req -new -key openfire_key.pem -out openfire.csr -days 3650
Enter pass phrase for openfire_key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Guangdong
Locality Name (eg, city) [Default City]:Guangzhou
Organization Name (eg, company) [Default Company Ltd]:openfire
Organizational Unit Name (eg, section) []:wzh.studio
Common Name (eg, your name or your server's hostname) []:openfire's domain
Email Address []:openfire@gmail.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:可以不填寫,按回車就ok
An optional company name []:可以不填寫,按回車就ok
//使用ca簽名,按照提示輸入兩個(gè)yes回車就ok了
# openssl ca -in openfire.csr -out openfire.pem
著重注意,在生成簽名請(qǐng)求的時(shí)候,Common Name屬性,必須輸入openfire服務(wù)器的domain,不要輸入openfire服務(wù)器的hostName
成功生成openfire.pem內(nèi)容以后,需要將服務(wù)器的私鑰內(nèi)容、私鑰密碼還有簽名證書內(nèi)容配置到/opt/openfire/resource/security/keystore容器中。如下圖順序配置



配置安卓客戶端,信任自簽名CA根證書
配置完服務(wù)器的證書以后,放置到項(xiàng)目中assert文件夾中,在客戶端連接openfire服務(wù)器時(shí)候開啟TLS連接配置。代碼如下
//要求連接必須使用TLS,builder是XMPPTCPConnectionConfiguration.Builder對(duì)象
builder.setSecurityMode(ConnectionConfiguration.SecurityMode.required);
//信任所有簽名證書,開啟這個(gè)就不要在客戶端安裝自簽名的CA機(jī)構(gòu)根證書了。這里關(guān)閉它
//TLSUtils.acceptAllCertificates(builder);
//****************************** 信任服務(wù)器簽發(fā)機(jī)構(gòu)的根證書開始 ***************************************//
//證書工廠。此處指明證書的類型
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
//創(chuàng)建一個(gè)證書庫(kù)
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
//取得SSL的SSLContext實(shí)例
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//cacert.cer是自簽名的cacert.pem根證書轉(zhuǎn)格式轉(zhuǎn)換成cer格式的
InputStream tis = getAssets().open("cacert.cer");
keyStore.setCertificateEntry("0", certificateFactory.generateCertificate(tis));
trustManagerFactory.init(keyStore);
//****************************** 信任服務(wù)器簽發(fā)機(jī)構(gòu)的根證書結(jié)束 ***************************************//
//設(shè)置客戶端信任的機(jī)構(gòu)根證書
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
builder.setCustomSSLContext(sslContext);
到了這一步,就完成了Android客戶端和openfire服務(wù)器的單向TLS安全通訊
雙向TLS通訊
在了解完以上知識(shí)以后,實(shí)現(xiàn)Android客戶端和openfire服務(wù)器的雙向TLS安全通訊也是十分簡(jiǎn)單了,只要簽發(fā)一個(gè)客戶端證書,安裝到Android手機(jī)上,最后讓openfire服務(wù)器信任這一個(gè)機(jī)構(gòu)即可。但是這種做法很少見的,將客戶端的私鑰和簽名證書存儲(chǔ)在手機(jī)上是挺危險(xiǎn)的。
自簽名客戶端證書
生成Android端使用keytool工具,命令行如下
//創(chuàng)建`/home/keys/client`目錄,進(jìn)入存放client密鑰文件的文件夾
# cd /home/keys/client
//生成客戶端密鑰對(duì),存儲(chǔ)到client.keystore,我的密碼是123456,自定義的
# keytool -genkey -alias client -keysize 2048 -validity 3650 -keyalg RSA -keystore client.keystore
//生成客戶端簽名申請(qǐng)文件
# keytool -certreq -alias client -sigalg SHA1withRSA -file client.csr -keystore client.keystore
//CA自簽名
# openssl ca -in client.csr -out client.cer -days -3650
//導(dǎo)入CA根證書到客戶端keystore中
# keytool -import -v -trustcacerts -alias ca_root -file /etc/pki/CA/cacert.pem -storepass 123456 -keystore client.keystore
//導(dǎo)入CA頒發(fā)的數(shù)字證書到keystore中
# keytool -import -v -alias client -file client.cer -keystore client.keystore
導(dǎo)入keystore到客戶端中
因?yàn)閗eystore的版本問(wèn)題,需要下載一個(gè)軟件KeyStore Explorer修改keystore的版本位BKS-V1,因?yàn)锳ndroid只支持這個(gè)。KeyStore Explorer的地址下載地址
然后在SSLContext中添加為客戶端的證書,將client.keystore文件放到assert目錄下
//用來(lái)安裝客戶端證書,用戶雙向認(rèn)證的。必須在服務(wù)器中信任該客服端證書的簽發(fā)機(jī)構(gòu)根證書
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore kks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream kis = getAssets().open("client.keystore");
kks.load(kis, "123456".toCharArray());
keyManagerFactory.init(kks, "123456".toCharArray());
//****************************** 安裝客戶端證書結(jié)束 ***************************************//
//設(shè)置SSLContext本地證書文件,還有信任根證書庫(kù)
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
builder.setCustomSSLContext(sslContext);
openfire服務(wù)器開啟雙向認(rèn)證
-
在openfire服務(wù)器上開啟強(qiáng)制使用TLS連接,強(qiáng)制雙向認(rèn)證
在openfire服務(wù)器上開啟強(qiáng)制使用TLS連接
在openfire服務(wù)器上開啟強(qiáng)制使用TLS連接 -
將用于自簽名的CA根證書設(shè)置到信任證書列表中,根證書位于
/etc/pki/CA/cacert.pem
將用于自簽名的CA根證書設(shè)置到信任證書列表中
將用于自簽名的CA根證書設(shè)置到信任證書列表中
將用于自簽名的CA根證書設(shè)置到信任證書列表中 - 重啟openfire服務(wù)器,手機(jī)客戶端重新登錄,完成與openfire服務(wù)器的雙向TLS通訊
參考文章
感謝以下文章作者,排名不分先后
Configure SSL/TLS certificate trust for XMPP with a trusted CA (for client-to-server channel security) the non-UI (stable) way
Weblogic服務(wù)器自簽名SSL證書解決iOS7.1企業(yè)應(yīng)用部署問(wèn)題
總結(jié)之:CentOS6.5下openssl加密解密及CA自簽頒發(fā)證書詳解




