maven导入:
javax.sip
jain-sip-ri
1.3.0-91
provided
log4j
log4j
1.2.17
认证流程:
1.客户端向服务端发送Register请求;
2.服务端返回401并带有WWW-Authenticate头消息;
3.客户端根据WWW-Authenticate里的信息做摘要认证,生成Authorization头消息并添加,再次发送Register;
4.服务端根据收到的Authorization头消息验证,返回200 OK结果;
WWW-Authenticate: Digest realm="xx",qop="auth,auth-int",nonce="xx",opaque="xx"
Authorization: Digest username="xx",realm="xx",qop="auth",nonce="xx",uri="path",cnonce="xx",nc=00000001,response="xx",opaque="xx"
摘要公式(MD5为例):
response=MD5(HA1:HD:HA2)
注:algorithm为MD5或者MD5-sess或者未定义,都是MD5算法;string类型字段加密时不带引号;
HA1定义:
1.如果algorithm是MD5或者未定义,则:
HA1=username:realm:password
2.如果algorithm是MD5-sess,则:
HA1=username:realm:password:nonce:cnonce
HA2定义:
1.如果qop是auth或者未定义,则:
HA2=Method:uri
2.如果qop是auth-int,则:
HA2=Method:uri:MD5(包体)
HD定义:
1.如果qop有值auth或者auth-int,则:
HD=nonce:nc:cnonce:qop
2.如果qop未定义,则:
HD=nonce
服务端Demo:
/**
* 2018年12月13日下午2:55:40
*/
package testJainSip;
import java.text.ParseException;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Set;
import java.util.TooManyListenersException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransportNotSupportedException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.DateHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import gov.nist.javax.sip.header.AuthenticationHeader;
/**
* @author XWF
*
*/
public class JainSipServer implements SipListener{
SipStack sipstack = null;
HeaderFactory hf = null;
AddressFactory af = null;
MessageFactory mf = null;
SipProvider sipProvider = null;
/**
* 保存正在注册的用户,注册第一步的
*/
private static Set registingId = new HashSet<>();
/**
* 保存当前注册的用户,注册成功的
*/
private static Hashtable registedContactURI = new Hashtable<>();
public static void main(String[] args) {
JainSipServer test = new JainSipServer();
test.init();
}
public void init() {
Properties prop = new Properties();
prop.setProperty("javax.sip.STACK_NAME", "teststackname");
// prop.setProperty("javax.sip.IP_ADDRESS", "127.0.0.1");
// prop.setProperty("javax.sip.OUTBOUND_PROXY", "127.0.0.1:8888/UDP");
// You need 16 for logging traces. 32 for debug + traces.
// Your code will limp at 32 but it is best for debugging.
prop.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
prop.setProperty("gov.nist.javax.sip.DEBUG_LOG", "siptestdebug.txt");
prop.setProperty("gov.nist.javax.sip.SERVER_LOG", "siptestlog.txt");
SipFactory sf = SipFactory.getInstance();
sf.setPathName("gov.nist");
try {
sipstack = sf.createSipStack(prop);
} catch (PeerUnavailableException e) {
e.printStackTrace();
}
try {
hf = sf.createHeaderFactory();
af = sf.createAddressFactory();
mf = sf.createMessageFactory();
ListeningPoint listeningPoint = sipstack.createListeningPoint("192.168.1.30", 5060, "udp");
sipProvider = sipstack.createSipProvider(listeningPoint);
sipProvider.addSipListener(this);
System.out.println("服务启动完成。。。");
} catch (TransportNotSupportedException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
} catch (TooManyListenersException e) {
e.printStackTrace();
} catch (ObjectInUseException e) {
e.printStackTrace();
} catch (PeerUnavailableException e) {
e.printStackTrace();
}
}
//Listener实现
@Override
public void processRequest(RequestEvent requestEvent) {
Request request = requestEvent.getRequest();
if(null == request) {
System.out.println("收到的requestEvent.getRequest() is null.");
return ;
}
System.out.println(">>>>>收到的request内容是\n"+request);
switch(request.getMethod().toUpperCase()){
case Request.INVITE:
System.out.println("收到INVITE的请求");
break;
case Request.REGISTER:
System.out.println("收到REGISTER的请求");
doRegister(request,requestEvent);
break;
case Request.SUBSCRIBE:
System.out.println("收到SUBSCRIBE的请求");
break;
case Request.ACK:
System.out.println("收到ACK的请求");
break;
case Request.BYE:
System.out.println("收到BYE的请求");
break;
case Request.CANCEL:
System.out.println("收到CANCEL的请求");
break;
default:
System.out.println("不处理的requestMethod:"+request.getMethod().toUpperCase());
}
}
@Override
public void processResponse(ResponseEvent responseEvent) {
Response response = responseEvent.getResponse();
if(null == response) {
System.out.println("response is null.");
return ;
}
System.out.println("收到的Response is :"+response);
ClientTransaction clientTransaction = responseEvent.getClientTransaction();
Request request = clientTransaction.getRequest();
System.out.println("收到的Response for request:"+request);
if(response.getStatusCode() == Response.TRYING) {
System.out.println("收到的response is 100 TRYING");
return ;
}
switch(request.getMethod().toUpperCase()) {
case Request.INVITE:
System.out.println("收到INVITE的响应");
break;
case Request.BYE:
System.out.println("收到BYE的响应");
break;
case Request.CANCEL:
System.out.println("收到CANCEL的响应");
break;
default:
System.out.println("不处理的response的请求类型:"+request.getMethod().toUpperCase());
}
}
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
// TODO Auto-generated method stub
}
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
// TODO Auto-generated method stub
}
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
// TODO Auto-generated method stub
}
private void doRegister(Request request, RequestEvent requestEvent) {
if(null == request || null == requestEvent) {
System.out.println("无法处理REGISTER请求,request="+request+",event="+requestEvent);
return ;
}
ServerTransaction serverTransactionId = requestEvent.getServerTransaction();
try {
Response response = null;
ToHeader toHead = (ToHeader) request.getHeader(ToHeader.NAME);
Address toAddress = toHead.getAddress();
URI toURI = toAddress.getURI();
SipURI sipURI_to = (SipURI) toURI;
String toUserId = sipURI_to.getUser();
System.out.println("注册的toUserId是"+toUserId);
ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME);
Address contactAddr = contactHeader.getAddress();
URI contactURI = contactAddr.getURI();
System.out.println("注册 from: " + toURI + " request str: " + contactURI);
if(null == toUserId || "".equals(toUserId)) {
System.out.println("无法识别的userId,不处理。");
return ;
}
int expires = request.getExpires().getExpires();
// 如果expires不等于0,则为注册,否则为注销。
if (expires != 0 || contactHeader.getExpires() != 0) {//注册
if(registedContactURI.containsKey(toUserId)) {//已经注册了
System.out.println("已经注册过了 user=" + toURI);
}else {//不是注册成功状态
if(registingId.contains(toUserId)) {//是第二次注册
System.out.println("第二次注册 register user=" + toURI);
// 验证AuthorizationHeader摘要认证信息
AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
boolean authorizationResult = false;
if(null != authorizationHeader) {//验证
String username = authorizationHeader.getUsername();
String realm = authorizationHeader.getRealm();
String nonce = authorizationHeader.getNonce();
URI uri = authorizationHeader.getURI();
String res = authorizationHeader.getResponse();
String algorithm = authorizationHeader.getAlgorithm();
System.out.println("Authorization信息:username="+username+",realm="+realm+",nonce="+nonce+",uri="+uri+",response="+res+",algorithm="+algorithm);
if(null==username || null==realm || null==nonce || null==uri || null==res || null==algorithm) {
System.out.println("Authorization信息不全,无法认证。");
}else {
// 比较Authorization信息正确性
String A1 = MD5Util.MD5(username+":"+realm+":12345678");
String A2 = MD5Util.MD5("REGISTER:sip:[email protected]:5060");
String resStr = MD5Util.MD5(A1+":"+nonce+":"+A2);
if(resStr.equals(res)) {
//注册成功,标记true
authorizationResult = true;
}
}
}
registingId.remove(toUserId);//不管第二次是否成功都移除,失败要从头再来
// 验证成功加入成功注册列表,失败不加入
if(authorizationResult) {//注册成功
System.out.println("注册成功?");
registedContactURI.put(toUserId, contactURI);
//返回成功
response = mf.createResponse(Response.OK, request);
DateHeader dateHeader = hf.createDateHeader(Calendar.getInstance());
response.addHeader(dateHeader);
System.out.println("返回注册结果 response是\n" + response.toString());
if (serverTransactionId == null) {
serverTransactionId = sipProvider.getNewServerTransaction(request);
serverTransactionId.sendResponse(response);
// serverTransactionId.terminate();
// System.out.println("register serverTransaction: " + serverTransactionId);
} else {
System.out.println("processRequest serverTransactionId is null.");
}
}else {//注册失败
System.out.println("注册失败?");
//返回失败
response = mf.createResponse(Response.FORBIDDEN, request);
System.out.println("返回注册结果 response是\n" + response.toString());
if (serverTransactionId == null) {
serverTransactionId = sipProvider.getNewServerTransaction(request);
serverTransactionId.sendResponse(response);
} else {
System.out.println("processRequest serverTransactionId is null.");
}
}
}else {//是第一次注册
System.out.println("首次注册 user=" + toURI);
// 加入registing列表
registingId.add(toUserId);
//发送响应
response = mf.createResponse(Response.UNAUTHORIZED, request);
String realm = "zectec";
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
String callId = callIdHeader.getCallId();
String nonce = MD5Util.MD5(callId+toUserId);
WWWAuthenticateHeader wwwAuthenticateHeader = hf.createWWWAuthenticateHeader("Digest realm=\""+realm+"\",nonce=\""+nonce+"\"");
response.setHeader(wwwAuthenticateHeader);
System.out.println("返回注册结果 response是\n" + response.toString());
if (serverTransactionId == null) {
serverTransactionId = sipProvider.getNewServerTransaction(request);
serverTransactionId.sendResponse(response);
// serverTransactionId.terminate();
// System.out.println("register serverTransaction: " + serverTransactionId);
} else {
System.out.println("processRequest serverTransactionId is null.");
}
}
}
} else {//注销
System.out.println("注销 user=" + toURI);
//发送ok响应
response = mf.createResponse(Response.OK, request);
System.out.println("返回注销结果 response : " + response.toString());
if (serverTransactionId == null) {
serverTransactionId = sipProvider.getNewServerTransaction(request);
serverTransactionId.sendResponse(response);
// serverTransactionId.terminate();
System.out.println("register serverTransaction: " + serverTransactionId);
} else {
System.out.println("processRequest serverTransactionId is null.");
}
//移除
registingId.remove(toUserId);
registedContactURI.remove(toUserId);
}
} catch (ParseException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
}
}
}
客户端Demo:
package testJainSip;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
import java.util.TooManyListenersException;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransportNotSupportedException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.DateHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.Header;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
public class JainSipClient {
private SipStack sipStack;
private SipFactory sipFactory;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private SipProvider sipProvider;
private Dialog dialog;
String ip = "192.168.1.30";
int port = 5061;
String uname="Tom";
public static void main(String[] args) {
JainSipClient client = new JainSipClient();
client.init();
client.sendMessage("Tom","192.168.1.30:5061","Tom","192.168.1.30:5061","");
}
public void init(){
try {
Properties prop = new Properties();
prop.setProperty("javax.sip.STACK_NAME", "teststackname");
prop.setProperty("javax.sip.IP_ADDRESS", ip);
prop.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
prop.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sipclientdebug.txt");
prop.setProperty("gov.nist.javax.sip.SERVER_LOG", "sipclientlog.txt");
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
sipStack = sipFactory.createSipStack(prop);
headerFactory = sipFactory.createHeaderFactory();
addressFactory = sipFactory.createAddressFactory();
messageFactory = sipFactory.createMessageFactory();
ListeningPoint listeningpoint_udp =sipStack.createListeningPoint(port, "udp");
// ListeningPoint listeningponit_tcp =sipStack.createListeningPoint(port, "tcp");
sipProvider = sipStack.createSipProvider(listeningpoint_udp);
ClientListener listener = new ClientListener(addressFactory,headerFactory,messageFactory,sipProvider);
sipProvider.addSipListener(listener);
// sipProvider = sipStack.createSipProvider(listeningponit_tcp);
// sipProvider.addSipListener(listener);
System.out.println("client init finished.");
} catch (PeerUnavailableException | TransportNotSupportedException | ObjectInUseException
| InvalidArgumentException | TooManyListenersException e) {
e.printStackTrace();
}
}
public void sendMessage(String fromUserName,String fromIpPort,String toUserName,String toIpPort,String message){
try {
//requestURI
SipURI requestSipURI = addressFactory.createSipURI("gov.nist","192.168.1.30:5060");
requestSipURI.setTransportParam("udp");
//from
SipURI fromSipURI = addressFactory.createSipURI(fromUserName, fromIpPort);
Address fromAddress = addressFactory.createAddress(fromSipURI);
fromAddress.setDisplayName("a");
FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,"mytag");
//to
SipURI toSipURI = addressFactory.createSipURI(toUserName, toIpPort);
Address toAddress = addressFactory.createAddress(toSipURI);
toAddress.setDisplayName("b");
ToHeader toHeader = headerFactory.createToHeader(toAddress,null);
//via
ViaHeader viaHeader = headerFactory.createViaHeader(ip, port, "udp", "branchingbranching");
List viaHeaderList = new ArrayList<>();
viaHeaderList.add(viaHeader);
//callid,cseq,maxforwards
CallIdHeader callIdHeader = sipProvider.getNewCallId();
CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.REGISTER);
MaxForwardsHeader maxForwardsHeader = headerFactory.createMaxForwardsHeader(70);
//
Request request = messageFactory.createRequest(requestSipURI, Request.REGISTER, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaderList, maxForwardsHeader);
//contact
SipURI contactURI = addressFactory.createSipURI(fromUserName, fromIpPort);
contactURI.setPort(port);
Address contactAddress = addressFactory.createAddress(contactURI);
contactAddress.setDisplayName(uname);
ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
request.addHeader(contactHeader);
//expires
ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(3600);
request.addHeader(expiresHeader);
// ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("text","plain");
// request.setContent(message,contentTypeHeader);
System.out.println(request);
sipProvider.sendRequest(request);
// //
// ClientTransaction trans = sipProvider.getNewClientTransaction(request);
// dialog = trans.getDialog();
// trans.sendRequest();
// //
// request = dialog.createRequest(Request.MESSAGE);
// request.setHeader(contactHeader);
// request.setContent(message, contentTypeHeader);
// ClientTransaction ctrans = sipProvider.getNewClientTransaction(request);
// ctrans.sendRequest();
} catch (ParseException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
}
}
}
class ClientListener implements SipListener{
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private SipProvider sipProvider;
public ClientListener(AddressFactory addressFactory, HeaderFactory headerFactory, MessageFactory messageFactory,
SipProvider sipProvider) {
super();
this.addressFactory = addressFactory;
this.headerFactory = headerFactory;
this.messageFactory = messageFactory;
this.sipProvider = sipProvider;
}
@Override
public void processRequest(RequestEvent requestEvent) {
System.out.println("processRequest执行");
Request request = requestEvent.getRequest();
if(null == request) {
System.out.println("requestEvent.getRequest() is null.");
return ;
}
System.out.println("request内容是\n"+request);
}
@Override
public void processResponse(ResponseEvent responseEvent) {
System.out.println("processResponse执行");
Response response = responseEvent.getResponse();
if(null == response) {
System.out.println("response is null.");
return ;
}
System.out.println("返回码:"+response.getStatusCode());
System.out.println("Response is :"+response);
WWWAuthenticateHeader wwwHeader = (WWWAuthenticateHeader) response.getHeader(WWWAuthenticateHeader.NAME);
if(null != wwwHeader) {
String realm = wwwHeader.getRealm();
String nonce = wwwHeader.getNonce();
String A1 = MD5Util.MD5("Tom:"+realm+":12345678");
String A2 = MD5Util.MD5("REGISTER:sip:[email protected]:5060");
String resStr = MD5Util.MD5(A1+":"+nonce+":"+A2);
try {
//requestURI
SipURI requestSipURI = addressFactory.createSipURI("gov.nist","192.168.1.30:5060");
requestSipURI.setTransportParam("udp");
//from
SipURI fromSipURI = addressFactory.createSipURI("Tom", "192.168.1.30:5061");
Address fromAddress = addressFactory.createAddress(fromSipURI);
fromAddress.setDisplayName("a");
FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,"mytag2");
//to
SipURI toSipURI = addressFactory.createSipURI("Tom", "192.168.1.30:5061");
Address toAddress = addressFactory.createAddress(toSipURI);
toAddress.setDisplayName("b");
ToHeader toHeader = headerFactory.createToHeader(toAddress,null);
//via
ViaHeader viaHeader = headerFactory.createViaHeader("192.168.1.30", 5061, "udp", "branchingbranching");
List viaHeaderList = new ArrayList<>();
viaHeaderList.add(viaHeader);
//callid,cseq,maxforwards
CallIdHeader callIdHeader = sipProvider.getNewCallId();
CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(2L, Request.REGISTER);
MaxForwardsHeader maxForwardsHeader = headerFactory.createMaxForwardsHeader(70);
//
Request request = messageFactory.createRequest(requestSipURI, Request.REGISTER, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaderList, maxForwardsHeader);
//contant
SipURI contantURI = addressFactory.createSipURI("Tom", "192.168.1.30:5061");
contantURI.setPort(5061);
Address contantAddress = addressFactory.createAddress(contantURI);
contantAddress.setDisplayName("abc");
ContactHeader contactHeader = headerFactory.createContactHeader(contantAddress);
request.addHeader(contactHeader);
//expires
ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(3600);
request.addHeader(expiresHeader);
ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("text","plain");
request.setContent("",contentTypeHeader);
AuthorizationHeader aHeader = headerFactory.createAuthorizationHeader("Digest");
aHeader.setUsername("Tom");
aHeader.setRealm(realm);
aHeader.setNonce(nonce);
aHeader.setURI(fromSipURI);
aHeader.setResponse(resStr);
aHeader.setAlgorithm("MD5");
request.addHeader(aHeader);
System.out.println(request);
sipProvider.sendRequest(request);
} catch (ParseException | InvalidArgumentException | SipException e) {
e.printStackTrace();
}
}
}
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
// TODO Auto-generated method stub
}
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
System.out.println("processTransactionTerminated执行");
}
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
System.out.println("processDialogTerminated执行");
}
}
结果:
参考:
https://www.oracle.com/technetwork/cn/articles/oem/introduction-jain-sip-090386-zhs.html
https://blog.csdn.net/gredn/article/details/72847197
https://www.cnblogs.com/wenjingu/p/4019434.html
https://blog.csdn.net/jszj/article/details/8918967?from=singlemessage
https://www.cnblogs.com/fengyv/archive/2013/02/05/2892729.html