`
youzifei
  • 浏览: 65943 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

openfire xmpp sasl 浅析

阅读更多

SASL全称Simple Authentication and Security Layer,是一种用来扩充C/S模式验证能力的机制。在Postfix可以利用SASL来判断用户是否有权使用转发服务,或是辨认谁在使用你的服务器。
SASL提供了一个通用的方法为基于连接的协议增加验证支持,而XMPP使用了一个普通的XML名字空间来满足SASL的需要。

在xmpp协议下客户端和服务器端的交换报文如下:
1.客户端发送xmpp流
[client]:<stream:stream to="192.168.0.104" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
2.服务端响应
[server]:<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="192.168.1.212" id="8e73bf6e" xml:lang="en" version="1.0">
3.服务端发送支持链接的特性
[server]:<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>JIVE-SHAREDSECRET</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>
4.客户端选择 tls (Transport Layer Security 传输层安全)协议
[client]:<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
5.服务器端开始处理 tls
[server]:<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
6.客户端重新发送开始流
[client]:<stream:stream to="192.168.1.212" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
7.服务端响应
[server]:<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="192.168.1.212" id="8e73bf6e" xml:lang="en" version="1.0"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism><mechanism>JIVE-SHAREDSECRET</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>
8.客户端发送 sasl认证请求
[client]:<auth mechanism="DIGEST-MD5" xmlns="urn:ietf:params:xml:ns:xmpp-sasl"></auth>
9.服务端发送 询问 询问内容使用base64进行编码
[server]:<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09IjE5Mi4xNjguMS4yMTIiLG5vbmNlPSJNODU5ZE9PT3R6b21CVHA3S25zSlg2YTJIS0ZEcGdSZ0FWQ3hHY2twIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</challenge>
realm="192.168.1.212",nonce="M859dOOOtzomBTp7KnsJX6a2HKFDpgRgAVCxGckp",qop="auth",charset=utf-8,algorithm=md5-sess
10.客户端应答询问,应答内容使用base64进行编码
[client]:<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iamltbXkiLHJlYWxtPSIxOTIuMTY4LjEuMjEyIixub25jZT0iTTg1OWRPT090em9tQlRwN0tuc0pYNmEySEtGRHBnUmdBVkN4R2NrcCIsbmM9MDAwMDAwMDEsY25vbmNlPSJDRU9ienFnMTVJV1JSNkcrOFBLS2ZtbG81MUI2UzZaU040WXBHNXFxIixkaWdlc3QtdXJpPSJ4bXBwLzE5Mi4xNjguMS4yMTIiLG1heGJ1Zj02NTUzNixyZXNwb25zZT1iYjFkOWEwMWQ5NTBkYTFhOTIxNTI2ODMzYmUyZjE3Mixxb3A9YXV0aCxhdXRoemlkPSJqaW1teSI=</response>
charset=utf-8,username="jimmy",realm="192.168.1.212",nonce="M859dOOOtzomBTp7KnsJX6a2HKFDpgRgAVCxGckp",nc=00000001,cnonce="CEObzqg15IWRR6G+8PKKfmlo51B6S6ZSN4YpG5qq",digest-uri="xmpp/192.168.1.212",maxbuf=65536,response=bb1d9a01d950da1a921526833be2f172,qop=auth,authzid="jimmy"
11.服务端验证成功
[server]:<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cnNwYXV0aD1kZGZiZmVmYmRiZjZlYmZjOTQ4M2U0MWMyMDM4MTA5YQ==</success>
rspauth=ddfbfefbdbf6ebfc9483e41c2038109a
12.客户端第三次重新建立流
[client]:<stream:stream to="192.168.1.212" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
13.服务端响应
[server]:<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="192.168.1.212" id="8e73bf6e" 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"/></stream:features>
下面就是一些流程性的东西了

在sasl 是怎样进行challenge、response、success的呢?
在服务端
先生成saslserver,由server生成challenge,发送给client。
    Map<String, String> props=new TreeMap<String, String>();
    props.put(Sasl.QOP, "auth");
    SaslServer ss=Sasl.createSaslServer("DIGEST-MD5", "xmpp", "java.com", props, new ServerCallbackHandler());
    byte[] token=new byte[0];
    byte[] challenge=ss.evaluateResponse(token);

在客户端收到 challenge后生, 生成saslclient 处理challenge 生成response 在发给server
    SaslClient sc=Sasl.createSaslClient(new String[]{"DIGEST-MD5"}, "x", "xmpp", "java.com", null, new ClientCallbackHandler());
    byte response[];
    response=sc.evaluateChallenge(challenge);
server收到 应答后进行验证,验证通过后给客户端一个响应。
    ss.evaluateResponse(response);
    if(ss.isComplete()) {
        System.out.println("auth success");
    }

下面是sasl 的一个小例子,希望有助于理解sasl协议

package test.com.jimmy.tls;

import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import javax.security.sasl.SaslServer;

public class AuthMain {

    /**
     * @param args
     * @throws SaslException
     */

    public static void main(String[] args) throws SaslException {

        Map<String, String> props=new TreeMap<String, String>();

        props.put(Sasl.QOP, "auth");
        /**
         * 为指定机制创建一个 SaslServer。 Sasl.createSaslServer(String mechanism, String protocol, String serverName, Map<String,?> props,
         * CallbackHandler cbh)
         *  参数 mechanism - 非 null 的机制名。它必须是 SASL 机制的 IANA 注册名。(例如 "GSSAPI"、"CRAM-MD5")。
         *  protocol - 非 null 的协议(例如 "ldap")字符串名,将为它执行身份验证。
         *  serverName - 服务器的非 null 完全限定主机名。
         *   props - 可能为 null 的属性集,用于选择 SASL 机制并配置所选机制的身份验证交换。例如,如果 props 包含
         * Sasl.POLICY_NOPLAINTEXT 属性,且属性值为 "true",则所选的 SASL 机制必须不易受到简单被动式攻击的危害。除了在此类中所声明的标准属性外,也可以包括其他的可能特定于机制的属性。忽略与所选机制无关的属性。 cbh
         * - SASL 机制使用的回调处理程序(可能为 null),以便为完成身份验证而从应用程序/库获得更多的信息。例如,SASL 机制可能需要从调用方获得身份验证 ID、密码和域。使用 NameCallback 来请求身份验证 ID。使用
         * PasswordCallback 来请求密码。如果要从域列表中选择,则使用 RealmChoiceCallback 来请求域,如果必须输入域,则使用 RealmCallback 来请求域。
         */
        SaslServer ss=Sasl.createSaslServer("DIGEST-MD5", "xmpp", "java.com", props, new ServerCallbackHandler());

        byte[] token=new byte[0];

        byte[] challenge=ss.evaluateResponse(token);

        SaslClient sc=
            Sasl.createSaslClient(new String[]{"DIGEST-MD5"}, "x", "xmpp", "java.com", null, new ClientCallbackHandler());

        byte response[];

        if(challenge != null) {

            response=sc.evaluateChallenge(challenge);

        } else {

            response=sc.evaluateChallenge(null);

        }

        ss.evaluateResponse(response);

        if(ss.isComplete()) {

            System.out.println("auth success");

        }

    }

}

class ClientCallbackHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        for(int i=0; i < callbacks.length; i++) {
            if(callbacks[i] instanceof NameCallback) {
                NameCallback ncb=(NameCallback)callbacks[i];
                ncb.setName("tony1");
            } else if(callbacks[i] instanceof PasswordCallback) {
                PasswordCallback pcb=(PasswordCallback)callbacks[i];
                pcb.setPassword("admin1s".toCharArray());
            } else if(callbacks[i] instanceof RealmCallback) {
                RealmCallback rcb=(RealmCallback)callbacks[i];
                rcb.setText("java.com");
            } else {
                throw new UnsupportedCallbackException(callbacks[i]);
            }

        }

    }

}

class ServerCallbackHandler implements CallbackHandler {

    public ServerCallbackHandler() {

    }

    public void handle(final Callback[] callbacks) throws IOException,

    UnsupportedCallbackException {

        for(Callback callback: callbacks) {

            if(callback instanceof RealmCallback) {
                System.out.println("RealmCallback" + ((RealmCallback)callback).getDefaultText());
            } else if(callback instanceof NameCallback) {
                System.out.println("NameCallback" + ((NameCallback)callback).getDefaultName());
            } else if(callback instanceof PasswordCallback) {
                ((PasswordCallback)callback).setPassword("admin1s".toCharArray());
            } else if(callback instanceof AuthorizeCallback) {
                AuthorizeCallback authCallback=((AuthorizeCallback)callback);
                authCallback.setAuthorized(true);
            } else {
                System.out.println(callback.getClass().getName());
                throw new UnsupportedCallbackException(callback, "Unrecognized Callback");

            }

        }

    }

}

分享到:
评论
1 楼 可以扯扯 2015-08-14  
好文要顶!看了之后茅塞顿开

相关推荐

Global site tag (gtag.js) - Google Analytics