SASL协议java实现

    SASL(简单授权和安全层)是一个互联网标准,它制定了一个鉴权协议,且在client和server之间建立连接。SASL定义了鉴权数据如何交换,但是并没有制定数据的内容。它是一个鉴权机制框架。

简单认证与安全层 (SASL) 是一个在网络协议中用来认证数据加密的构架。它把认证机制从程序中分离开, 理论上使用SASL的程序协议都可以使用SASL所支持的全部认证机制。认证机制可支持代理认证, 这让一个用户可以承担另一个用户的认证。 SASL同样提供数据安全层,这提供了数据完整验证数据加密。 DIGEST-MD5 提供了数据加密层,这是机制中的一个例子。支持SASL的应用程序通常也支持 传输层安全 (TLS) 作为对SASL提供的服务的补充。
    SASL是一种challange-response协议,server发布challenge到client,而client基于challange发送response。这种交换直到server被满足且b不再发布challage。challenge和response是任意长度的二进制标记,封装协议诸如LDAP、IMAP指定了这些标记如何被编码和交换的。例如LDAP指定了SASL标记被封装在LDAP request和response内。

一个SASL机制实现了一系列的要求和特性。已经制定的SASL机制包括:

    Java SASL根据这种应答和使用方式已经模板化了,SaslClient和SaslServer接口,分别代表了client-side和server-side的机制。应用程序通过代表challenge和response的字节数组,使用这种机制交互。server-side机制:不断发布challenge,处理response直到被满足,而client-side机制:不断评估challenge,且发布response直到server满足。

创建机制
String[] mechanisms = new String[]{"DIGEST-MD5", "PLAIN"};
//client端机制
SaslClient sc = Sasl.createSaslClient(mechanisms,authzid,\
    protocol, serverName, props, callbackHandler);
//server端机制
SaslServer ss = Sasl.createSaslServer(mechanism, protocol,\
    myName, props, callbackHandler);


传递输入到机制
  Java SASL是一个通用的框架,它必须能容纳多种不同类型的机制,每个机制需要用输入来初始化,且需要输入做进一步的事情。API提供了三种application传递input给机制的方式。
  1)普通输入参数。应用程序使用预定义参数来提供信息。对于sasl客户端机制,输入参数是授权ID、协议ID和server名称。对于sasl服务端机制,输入参数是协议id和server名称。
  2)属性参数。Java sasl api定义了一些标准属性,如 quality-of-protection, cipher strength和maximum buffer size。此参数可以以非标准属性(相对于特定机制)使用。
  3)Callbacks。使用回调handler参数来提供输入(不能被预定义)。当机制需要输入数据时,它使用callback提供数据。
使用机制
    一旦应用程序创建一个机制,它使用此机制来获取sasl tokens做点交换,客户端通过协议指示给server。有些协议允许客户端使用带可选的初始response请求。

/*客户端如何使用机制*/
// Get optional initial response
byte[] response = 
    (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[]) : null);
String mechanism = sc.getName();
 
// Send selected mechanism name and optional initial response to server
send(mechanism, response);
 
// Read response
msg = receive();
 
while (!sc.isComplete() && (msg.status == CONTINUE || msg.status == SUCCESS)) {
 
    // Evaluate server challenge
    response = sc.evaluateChallenge(msg.contents);
 
    if (msg.status == SUCCESS) {
        // done; server doesn't expect any more SASL data
        if (response != null) {
           throw new IOException(
               "Protocol error: attempting to send response after completion");
        }
    break;
    } else {
        send(mechanism, response);
    msg = receive();
    }
}


    一旦应用程序创建一个机制,它使用此机制来获取sasl tokens做点交换,客户端通过协议指示给server。有些协议允许客户端使用带可选的初始response请求。客户端应用程序不断循环使用机制来取得从server获得challenge的response,回送给server,直到机制或应用程序协议表明授权完成或机制不能获得challenge。

/**服务端使用机制*/
msg.receive();//读取包含机制和可选初始化response的请求
// 获得SaslServer来完成授权
SaslServer ss = Sasl.createSaslServer(msg.mechanism, 
    protocol, myName, props, callbackHandler);
// 迭代授权步骤
while (!ss.isComplete()) {//未完成,继续迭代
 
    try {
        // Process response
        byte[] challenge = sc.evaluateResponse(msg.contents);
 
        if (ss.isComplete()) {//完成
            send(mechanism, challenge, SUCCESS);
        } else {//未完成,发送
            send(mechanism, challenge, CONTINUE);
        msg.receive();//接受
        }
    } catch (SaslException e) {
        send(ERROR);
    sc.dispose();
    break;
    }
}

一个demo:

public class AuthMain {
    public static void main(String[] args) throws SaslException {
       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);
       SaslClient sc = Sasl.createSaslClient(new String[] { "DIGEST-MD5" },
              "tony", "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("tony");
           } else if (callbacks[i] instanceof PasswordCallback) {
              PasswordCallback pcb = (PasswordCallback) callbacks[i];
              pcb.setPassword("admin1".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) {
              //do your business
           } else if (callback instanceof NameCallback) {
             //do your business
           } else if (callback instanceof PasswordCallback) {
              ((PasswordCallback) callback).setPassword("admin1"
                     .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");
           }
       }
    }
}

 

posted @ 2023-02-09 10:26  車輪の唄  阅读(155)  评论(0编辑  收藏  举报  来源