Jain-Sip Authentication

Martin picture Martin · Oct 23, 2012 · Viewed 7.3k times · Source

I'm working with the Jain Sip library, trying to create a connection to an asterisk server. I've been working with the textclient example app from this page: http://www.oracle.com/technetwork/articles/entarch/introduction-jain-sip-090386.html

but this doesn't actually authenticate with the server. I can send messages to users on the server, but I normally require a user/password authentication on the server.

As far as I can tell I have to use the "javax.sip.OUTBOUND_PROXY" property, but I can't find any documentation on how to set a username or password. Has anyone else had any success with this?

Answer

11101101b picture 11101101b · Sep 17, 2013

The best example I have found on registration is found here. Here is the gist of if just in case the link dries-up one day:

REGISTER request is used to update the current location of a user on the REGISTRAR server. The application sends a REGISTER message informing the server its current location which in turn is stored in location server. When a caller calls a user the proxy server uses this information to find the location of the callee.

Register request should be sent by the client periodically. The validity of the REGISTER request is determined by the Expires header.

Flow

image

Sample Request

REGISTER sip:sip.linphone.org SIP/2.0
Call-ID: [email protected]
CSeq: 1 REGISTER
From: <sip:[email protected]>;tag=-1427592833
To: <sip:[email protected]>
Max-Forwards: 70
Via: SIP/2.0/TCP 223.1.1.128:5060;branch=z9hG4bK-323532-2a454f4ec2a4213f6d6928eba479521d
Contact: <sip:[email protected];transport=tcp>
Content-Length: 0

Now lets see how to construct the above request using the NIST SIP Stack.

First step is to create a class that implements the SIPListener. Make sure your SIP Stack is Initializing NIST JAIN SIP Stack.

  1. Create Call-ID header

    CallIdHeader callIdHeader = this.sipProvider.getNewCallId();

  2. Create CSeq header

    CSeqHeader cSeqHeader = this.headerFactory.createCSeqHeader(cseq, "REGISTER");

  3. Create From header

    Address fromAddress = addressFactory.createAddress("sip:" + username + '@' + server); FromHeader fromHeader = this.headerFactory.createFromHeader(fromAddress, String.valueOf(this.tag));

  4. Create To Header

    ToHeader toHeader = this.headerFactory.createToHeader(fromAddress, null);

  5. Create Max-Forwards header

    MaxForwardsHeader maxForwardsHeader = this.headerFactory.createMaxForwardsHeader(70);

  6. Create a Via header

    ArrayList viaHeaders = new ArrayList(); ViaHeader viaHeader = this.headerFactory.createViaHeader(this.ip, this.port, "tcp", null); viaHeaders.add(viaHeader);

  7. Create contact header

    this.contactAddress = this.addressFactory.createAddress("sip:" + this.username + '@' + this.ip + "transport=tcp");

    // Create the contact header used for all SIP messages. this.contactHeader = this.headerFactory.createContactHeader(contactAddress);

Once all the headers are created it is time to create the request itself.

request = this.messageFactory.createRequest("REGISTER sip:" + server + "SIP/2.0\r\n\r\n"); 
request.addHeader(callIdHeader);
request.addHeader(cSeqHeader); 
request.addHeader(fromHeader);
request.addHeader(toHeader); 
request.addHeader(maxForwardsHeader);
request.addHeader(viaHeader); 
request.addHeader(contactHeader);

Now that the request object is created with all the necessary headers it is time to send the request.

inviteTid = sipProvider.getNewClientTransaction(request); // send the request out. 
inviteTid.sendRequest();

Once the request is sent successfully the response will be passed to the application using the processResponse callback in SIPListener.

public void processResponse(ResponseEvent responseEvent) { 
  int statusCode = responseEvent.getResponse().getStatusCode(); 
}

Code

public void register(Response response) {
  try {
  cseq++;
  ArrayList viaHeaders = new ArrayList();
  ViaHeader viaHeader = this.headerFactory.createViaHeader(this.ip,
  this.port, "tcp", null);
  viaHeaders.add(viaHeader);
  // The "Max-Forwards" header.
  MaxForwardsHeader maxForwardsHeader = this.headerFactory
  .createMaxForwardsHeader(70);
  // The "Call-Id" header.
  CallIdHeader callIdHeader = this.sipProvider.getNewCallId();
  // The "CSeq" header.
  CSeqHeader cSeqHeader = this.headerFactory.createCSeqHeader(cseq,
  "REGISTER");

  Address fromAddress = addressFactory.createAddress("sip:"
  + username + '@' + server);

  FromHeader fromHeader = this.headerFactory.createFromHeader(
  fromAddress, String.valueOf(this.tag));
  // The "To" header.
  ToHeader toHeader = this.headerFactory.createToHeader(fromAddress,
  null);

  // this.contactHeader = this.headerFactory
  // .createContactHeader(contactAddress);

  request = this.messageFactory.createRequest("REGISTER sip:"
  + server + " SIP/2.0\r\n\r\n");
  request.addHeader(callIdHeader);
  request.addHeader(cSeqHeader);
  request.addHeader(fromHeader);
  request.addHeader(toHeader);
  request.addHeader(maxForwardsHeader);
  request.addHeader(viaHeader);
  request.addHeader(contactHeader);
  if (response != null) {
  retry = true;
  AuthorizationHeader authHeader = Utils.makeAuthHeader(headerFactory, response,
  request, username, password);
  request.addHeader(authHeader);
  }
  inviteTid = sipProvider.getNewClientTransaction(request);
  // send the request out.
  inviteTid.sendRequest();
  this.dialog = inviteTid.getDialog();
  // Send the request statelessly through the SIP provider.
  //            this.sipProvider.sendRequest(request);

  // Display the message in the text area.
  logger.debug("Request sent:\n" + request.toString() + "\n\n");
  } catch (Exception e) {
  // If an error occurred, display the error.
  e.printStackTrace();
  logger.debug("Request sent failed: " + e.getMessage() + "\n");
  }
}

You can also view the reference post on authentication here. Here is the gist of if just in case the link dries-up one day:

During a SIP request if the server responds with 401 Proxy Authentication Required or 401 Unauthorized then it means the client has to replay the same request again with MD5 challenge.

The client should use nonce value from the response header WWW-Authenticate.

WWW-Authenticate: Digest realm="sip.linphone.org", nonce="JbAO1QAAAAA3aDI0AADMobiT7toAAAAA", opaque="+GNywA==", algorithm=MD5, qop="auth"

The client should use nonce to generate the MD5 challenge and make the original request again with the Authorization header.

Steps to create the MD5 Challenge

  1. Create first MD5 hash using username + “:” + realm + “:” + password

    String a1 = username + ":" + realm + ":" + password; String ha1 = toHexString(mdigest.digest(a1.getBytes()));

  2. Create second MD5 hash using request_method + “:” + request_uri

    String a2 = request_method.toUpperCase() + ":" + request_uri; String ha2 = toHexString(mdigest.digest(a2.getBytes()));

  3. If qop in the response header is “auth” then the final MD5 hash is calculated using step 3a else if it is undefined or empty refer step 3b.

3a. Create the final MD5 string using ha1 + “:” + nonce + “:” + nonceCount + “:” + cNonce + “:” + qop + “:” + ha2

String finalStr = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce + ":" + qop + ":" + ha2; 
String response = toHexString(mdigest.digest(finalStr.getBytes()));

3b. Create the final MD5 string using ha1 + “:” + nonce + “:” + ha2

String finalStr = ha1 + ":" + nonce + ":" + ha2; 
String response = toHexString(mdigest.digest(finalStr.getBytes()));