本文從TLS安全傳輸層協(xié)議的簡單流程、如何生成自簽名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ǔ)概念作過多介紹紊撕。這邊會(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)行加密解密處理患整,以此來保證HTTP傳輸數(shù)據(jù)的安全拜效,因?yàn)镠TTP對(duì)于數(shù)據(jù)的傳輸是明文的。
這里對(duì)HTTPS連接做簡單描述各谚,其具體作用在TCP三次連接建立以后紧憾,由客戶端(這里是C&S模型)發(fā)出Client Hello的TLS連接建立請(qǐng)求,然后服務(wù)器會(huì)告訴客戶端:服務(wù)器支持什么加密算法昌渤,發(fā)給客戶端自己掏大價(jià)錢買來的簽名證書的內(nèi)容赴穗。
然后客戶端使用本地預(yù)裝或者人為加裝的CA根證書去驗(yàn)證服務(wù)器發(fā)來的簽名證書是否有效,如果安全有效那就bingo膀息,建立TLS連接般眉,否則斷開連接。
而XMPP協(xié)議潜支,在不使用TLS的時(shí)候甸赃,是一個(gè)明文的傳輸?shù)膮f(xié)議。其中的SASL協(xié)議只是起到了對(duì)鑒權(quán)數(shù)據(jù)(賬號(hào)密碼冗酿,或者其他用來確定登錄資源的數(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é)議來保駕護(hù)航了(新版本的XMPP傳輸報(bào)文不是明文的大审,也有一定的監(jiān)聽難度)蘸际。
關(guān)于CA自簽名
了解了TLS之后,因?yàn)檫@篇文章是出于學(xué)習(xí)目的徒扶,所以我們再學(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è)長度是2048的私鑰文件
# (umask 077; openssl genrsa -out private/cakey.pem 2048)
//生成一個(gè)自簽名證書核芽,有效期是3650天,時(shí)長可以自由更改酵熙,里面需要填寫很多信息
# opensslreq -new -x509 -key private/cakey.pem -out cacert.pem -days 3650
//生成存儲(chǔ)證書序列號(hào)的文件
# touch index.txt serial
//先自增1
# echo 01 > serial
自此轧简,CA自簽名證書搞定了,接下來就要給自己頒發(fā)一個(gè)openfire服務(wù)器證書了
生成openfire服務(wù)器證書匾二、自簽名哮独、配置
在此之前,簡單描述一下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ù)字證書)過的通訊對(duì)方就是可信機(jī)構(gòu)敏释,會(huì)有一個(gè)數(shù)字證書。只要被系統(tǒng)中預(yù)裝的某個(gè)CA結(jié)構(gòu)根證書驗(yàn)證通過了摸袁,就證明了通訊對(duì)方提供的公鑰是安全的(所以客戶端被做過手腳就不安全了)钥顽。
當(dāng)A和B使用TLS通訊的時(shí)候,A花錢去名叫C的CA機(jī)構(gòu)獲得了數(shù)字證書靠汁,在通訊的時(shí)候傳輸給B蜂大。B通過內(nèi)置的CA機(jī)構(gòu)根證書驗(yàn)證闽铐,鑒定這是真的A。之后使用A的公鑰奶浦,或者使用A的公鑰加密過的對(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ù)字證書就是用來證明申請(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è)空的密鑰容器了慈鸠。接下來我們要使用openssl還有keytool生成還有轉(zhuǎn)換openfire服務(wù)器的證書了
我把openfire服務(wù)器放置在/home/keys/openfire
目錄中蓝谨,同樣按照以下操作敲命令即可,注意敲那些前面帶有#的
//創(chuàng)建`/home/keys/openfire目錄青团,進(jìn)入該目錄
//生成openfire證書的密鑰對(duì)譬巫,接下來有個(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è)證書庫
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安全通訊也是十分簡單了梭纹,只要簽發(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的版本問題蝠引,需要下載一個(gè)軟件KeyStore Explorer修改keystore的版本位BKS-V1阳谍,因?yàn)锳ndroid只支持這個(gè)。KeyStore Explorer的地址下載地址
然后在SSLContext中添加為客戶端的證書螃概,將client.keystore文件放到assert目錄下
//用來安裝客戶端證書矫夯,用戶雙向認(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本地證書文件吊洼,還有信任根證書庫
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
builder.setCustomSSLContext(sslContext);
openfire服務(wù)器開啟雙向認(rèn)證
-
在openfire服務(wù)器上開啟強(qiáng)制使用TLS連接茧痒,強(qiáng)制雙向認(rèn)證
-
將用于自簽名的CA根證書設(shè)置到信任證書列表中,根證書位于
/etc/pki/CA/cacert.pem
- 重啟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)用部署問題
總結(jié)之:CentOS6.5下openssl加密解密及CA自簽頒發(fā)證書詳解