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机制包括:
- "EXTERNAL", 认证信息在内容中(例如已经使用IPsec或传输层安全的协议)
- "ANONYMOUS", 对与未认证的客人的访问
- "PLAIN", 一个简单明文密码机制。PLAIN取代了LOGIN 机制。
- "OTP", 一个临时密码机制。 OTP取代了SKEY机制。
- "SKEY", 一个S/KEY机制
- "CRAM-MD5", 一个简单的基于HMAC-MD5的询问应答机制。
- "DIGEST-MD5", 是一个HTTP兼容的,基于MD5的询问应答机制。DIGEST-MD5提供了数据层安全。
- "NTLM", 一个 NT LAN Manager认证机制。
- "GSSAPI", 通过通用安全服务应用程序层的Kerberos V5 协议的安全认证。GSSAPI 提供了数据安全层。
- GateKeeper Microsoft为Windows Live Messenger开发的一个询问应答机制。
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");
}
}
}
}