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?
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
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.
Create Call-ID header
CallIdHeader callIdHeader = this.sipProvider.getNewCallId();
Create CSeq header
CSeqHeader cSeqHeader = this.headerFactory.createCSeqHeader(cseq, "REGISTER");
Create From header
Address fromAddress = addressFactory.createAddress("sip:" + username + '@' + server); FromHeader fromHeader = this.headerFactory.createFromHeader(fromAddress, String.valueOf(this.tag));
Create To Header
ToHeader toHeader = this.headerFactory.createToHeader(fromAddress, null);
Create Max-Forwards header
MaxForwardsHeader maxForwardsHeader = this.headerFactory.createMaxForwardsHeader(70);
Create a Via header
ArrayList viaHeaders = new ArrayList(); ViaHeader viaHeader = this.headerFactory.createViaHeader(this.ip, this.port, "tcp", null); viaHeaders.add(viaHeader);
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
Create first MD5 hash using username + “:” + realm + “:” + password
String a1 = username + ":" + realm + ":" + password; String ha1 = toHexString(mdigest.digest(a1.getBytes()));
Create second MD5 hash using request_method + “:” + request_uri
String a2 = request_method.toUpperCase() + ":" + request_uri; String ha2 = toHexString(mdigest.digest(a2.getBytes()));
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()));