安卓xmpp简单介绍(登录)以及自签名证书的使用

本文介绍了安卓应用如何支持XMPP协议,包括asmack客户端库的使用和openfire服务端的配置。详细讲解了XMPP的DIGEST-MD5登录交互过程,并重点阐述了在SSL协议下,如何处理自签名证书的问题,包括生成合法证书、客户端加载证书的步骤,以确保安全的XMPP连接。

安卓xmpp简单介绍(登录)以及自签名证书的使用

概念

OSI七层模型

开放系统互连参考模型 (Open System Interconnect 简称OSI)是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制定的开放系统互连参考模型,为开放式互连信息系统提供了一种功能结构的框架。
它从低到高分别是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,具体参考[百度百科]。

七层模型的介绍网络上很多,这里不浪费篇幅,主要给初学者举几个例子,可能印象更深,初级java开发常接触到的协议以及所属的层分别为:

IP(Internet Protocol)-网络层

TCP(Transmission Control Protocol)-传输层

UDP(User Datagram Protocol)-传输层

SSL(Secure Sockets Layer)-传输层和应用层之间

TLS(Transport Layer Security)-传输层和应用层之间

HTTP(HyperText Transfer Protocol)-应用层

HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)-应用层

FTP(File Transfer Protocol)-应用层

SFTP(Secure File Transfer Protocol)-应用层

TELNET-应用层

DNS(Domain Name System)-应用层

XMPP

Extensible Messaging and Presence Protocol,(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。
这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。属于应用层范畴,在TCP或TLS之上进行数据的封装。

安卓应用支持xmpp

asmack(客户端)

asmack是一个开源软件,支持在安卓应用上封装实现xmpp协议的交互功能。近期由于smack在4之后支持了安卓,因此smack自2015年2月后不再更新,
asmack只有一个jar包,因此集成比较方便,本文示例代码仍旧以asmack为例(与smack大同小异)。
jar和源码:http://asmack.freakempire.de/4.0.7/

openfire(服务端)

openfire是开源的、基于可拓展通讯和表示协议(XMPP)、采用Java编程语言开发的实时协作服务器。 Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。
下载地址:https://www.igniterealtime.org/projects/openfire/

协议的介绍

网络上很多,本文不再赘述,贴几个随手能搜到的帖子:

XMPP-百度百科

XMPP协议实现原理介绍

示例代码

初始化
config = new ConnectionConfiguration(host, port, serviceName);
config.setDebuggerEnabled(true);
config.setCompressionEnabled(false);
config.setReconnectionAllowed(true);
config.setSecurityMode(SecurityMode.disabled); // 是否开启TLS
SASLAuthentication.registerSASLMechanism("MyMechanism", MyMechanism.class);// 新增自定义的登录校验机制
SASLAuthentication.supportSASLMechanism("MyMechanism", 0); // 设置登录校验器的序号,序号小的会优先选择

serviceName即openfire的domain,可以在openfire管理台上查到,一般为计算机名。

asmack默认集成了”DIGEST-MD5”、”PLAIN”、”ANONYMOUS”等几种校验器,具体参考源码:SASLAuthentication.java

登录
connection = new XMPPTCPConnection(config);
connection.addPacketListener(new PacketListener()
{
    @Override
    public void processPacket(Packet packet)
    {
        processReceivedMessage(packet);// 接收消息后的处理,可以根据消息类型做业务逻辑
    }
}, new PacketFilter()
{
    @Override
    public boolean accept(Packet packet)
    {
        return true;
    }
});
connection.connect();
connection.login(userName, password, resource);//resource为资源名,可以为空。

连接相关操作不能在主线程中进行。

userName为”JID”,假如我们在openfire上创建一个用户名为abc,那么JID为abc@domain。
实际测试登录方式为DIGEST-MD5时,userName带上@domain,”PLAIN”时可以只传abc。

DIGEST-MD5登录过程交互
  • 客户端打开stream流
<stream:stream to="mypcname-f"
    xmlns="jabber:client"
    xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
  • 服务端打开stream流
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream
    xmlns:stream="http://etherx.jabber.org/streams"
    xmlns="jabber:client" from="mypcname-f" id="akgn03i374" xml:lang="en" version="1.0">
  • 服务端返回特性列表
<stream:features>
    <starttls
        xmlns="urn:ietf:params:xml:ns:xmpp-tls">
    </starttls>
    <mechanisms
        xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        <mechanism>DIGEST-MD5</mechanism>
        <mechanism>SCRAM-SHA-1</mechanism>
        <mechanism>PLAIN</mechanism>
        <mechanism>ANONYMOUS</mechanism>
        <mechanism>CRAM-MD5</mechanism>
    </mechanisms>
    <compression
        xmlns="http://jabber.org/features/compress">
        <method>zlib</method>
    </compression>
    <auth
        xmlns="http://jabber.org/features/iq-auth"/>
        <register
            xmlns="http://jabber.org/features/iq-register"/>
</stream:features>
  • 客户端选择鉴权机制
<auth mechanism="DIGEST-MD5"
    xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
</auth>
  • 服务端返回挑战码
<challenge
    xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09ImYwMDE5NDYwMy1m...2Vzcw==
</challenge>
  • 客户端回应挑战
<response
    xmlns="urn:ietf:params:xml:ns:xmpp-sasl">dXNlcm5hbWU9ImZzdEBmMD......dlNKQVJKRzhTdWci
</response>
  • 服务端返回鉴权结果
<success
    xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cnNwYXV0aD0...U5NTc2MDIxMA==
</success>
  • 客户端重新打开stream流
<stream:stream to="mypcname-f"
    xmlns="jabber:client"
    xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
  • 服务器端重新打开stream流
<?xml version='1.0' encoding='UTF-8'?>
<stream:stream
    xmlns:stream="http://etherx.jabber.org/streams"
    xmlns="jabber:client" from="mypcname-f" id="akgn03i374" xml:lang="en" version="1.0">
  • 服务器端重新返回特性列表
<stream:features>
    <compression
        xmlns="http://jabber.org/features/compress">
        <method>zlib</method>
    </compression>
    <bind
        xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
    <session
        xmlns="urn:ietf:params:xml:ns:xmpp-session">
        <optional/>
    </session>
    <sm
        xmlns='urn:xmpp:sm:2'/>
    <sm
        xmlns='urn:xmpp:sm:3'/>
</stream:features>
  • 客户端发送资源绑定请求
<iq id='uhNnW-13' type='set'>
    <bind
        xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
        <resource>xxxResource</resource>
    </bind>
</iq
  • 服务器返回资源绑定响应
<iq type="result" id="uhNnW-13" to="mypcname-f/akgn03i374">
    <bind
        xmlns="urn:ietf:params:xml:ns:xmpp-bind">
        <jid>fst@mypcname-f/xxxResource</jid>
    </bind>
</iq
  • 客户端发送创建会话
<iq id='uhNnW-14' type='set'>
    <session
        xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
    </iq>
  • 服务端响应会话创建成功
<iq type="result" id="uhNnW-14" to="fst@mypcname-f/xxxResource"/>
  • 客户端发送初始化presence
<presence id='uhNnW-15'></presence>

支持SSL协议的登录

制作服务端和客户端证书

openfire默认会生成服务端的证书以及keysotre,但这个证书是颁给域名(默认是计算机名)的,我们在拿手机测试过程中很可能需要直接通过IP地址访问,这时候默认生成的证书在SSL/TLS握手过程中会被认为不合法,无法建立SSL/TLS连接。本文借解决该问题,简单描述如何使用自签名证书在TLS下进行XMPP登录。

证书不合法一般原因不外乎以下几种:签发机构不合法(自签名证书)、有效期过期、颁发的域名与访问域名不同等等,我们使用keytool命令制作合法的自签名证书,然后让客户端信任该证书公钥,解决本问题。

  • 生成自签名证书及keystore(服务器端使用),其中CN字段填写openfire所在的ip地址
keytool -genkey -alias mycert -keyalg RSA -keysize 1024 -keystore server.keystore -validity 365
  • 修改openfire设置,指向该keystore文件。

    菜单:服务器-TLS/SSL Certificates,修改其中的Identity Store的File字段,改为我们刚生成的server.keystore
  • 从keystore中导出证书公钥
keytool -export -alias mycert -keystore server.keystore -file mycert.crt
  • 将公钥证书导入另外一个keystore(可以不存在,会直接创建),供客户端使用
keytool -import -alias mycert -keystore client.keystore -storetype bks-v1 -file mycert.crt
-provider org.bouncycastle.jce.provider.BouncyCastleProvider
-providerpath C:/Users/mypcname/Desktop/tvms/bksprovider/bcprov-jdk15on-155.jar

默认keystore的类型为JKS,安卓要求使用BKS,这里需要额外下载一个jar包,并指定keystore类型为BKS

客户端代码打开TLS,并且加载这个证书
config.setSecurityMode(SecurityMode.enabled);
InputStream ins = null;
>文件均以放在src/main/assets下为例

方式一:加载keysotre文件,需要指定keystore密码

ins = OTTSDK.getApplicationContext().getAssets().open("client.keystore");
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(ins, "changeit".toCharArray());

方式二:加载证书文件,可以为X.509的crt或者pem格式。

ins = OTTSDK.getApplicationContext().getAssets().open("mycert.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate x509Certificate = (X509Certificate) cf.generateCertificate(ins);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("xmppCert", x509Certificate);

设置SSLContext

// set TrustManager and SSLContext.
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
config.setCustomSSLContext(sslContext);

引用

csdn指导文档,涉及客户端和服务端软件,以及客户端样例代码:
http://blog.csdn.net/h7870181/article/details/8653865

openfire使用和配置:
http://www.cnblogs.com/hoojo/archive/2012/05/17/2506769.html

Connecting to a server that uses a self signed certificate:
https://community.igniterealtime.org/thread/56000

Extensible Messaging and Presence Protocol (XMPP):
https://xmpp.org/rfcs/rfc3921.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值