Monthly Archives: September 2015

XMPP Client Server Setup and Programming

XMPP is a open XML technology for real-time communication. Applications are instant messaging, presence, media negotiation, whiteboarding, collaboration, lightweight middleware, content syndication, and generalized XML routing according to XMPP standards Foundation (XSF) .

Extensible Messaging and Presence Protocol (XMPP) is a communications protocol for message-oriented middleware based on XML (Extensible Markup Language). – wikipedia

XMPP Server

Some popular servers on XMPP are ejabbred ( written in erlang licensed by GPL2) and openfire ( written in Java licensed by Apache ). This article will show the installation steps for openfire on Ubuntu version 15 64 bit system

1.Install the tar from http://www.igniterealtime.org/downloads/index.jsp

Screenshot from 2015-09-25 15:12:02

2. Extract and move the folder to /opt

3. Goto bin and run  openfire server  with ./openfire start

Screenshot from 2015-09-24 12:46:12 (copy)

4. Gotot the web admin url http://localhost:9090/ .  For first time  the setup screen will appear

Screenshot from 2015-09-24 12:46:31

5.  Proceed with installation  .

Screenshot from 2015-09-24 12:46:12

It will show screens to select the mysql driver and database . Create a empty db name called openfiredb and add that to mysql url in setup screen of openfire

It will also request a administrator username and password I choose to give admin admin as the username and password alike .

6. change the interface inside of openfire.xml file in location /opt/openfire/conf

<network>
<interface>127.0.0.1</interface>
</network>

we can also review the mysql connection string

<database>
<defaultProvider>
<driver>com.mysql.jdbc.Driver</driver>
<serverURL>jdbc:mysql://127.0.0.1:3306/openfiredb?rewriteBatchedStatements=true</serverURL>
<username encrypted=”true”><<someval>></username>
<password encrypted=”true”> <<someval>></password>
<testSQL>select 1</testSQL>
<testBeforeUse>false</testBeforeUse>
<testAfterUse>false</testAfterUse>
<minConnections>5</minConnections>
<maxConnections>25</maxConnections>
<connectionTimeout>1.0</connectionTimeout>
</defaultProvider>
</database>

7. After the installation login to the server admin console with the admin username and password which is admin admin in our case

Screenshot from 2015-09-24 12:54:08

8.  Review the server settings etc from the admin web console

Screenshot from 2015-09-24 13:16:29

9. Incase the server setup did not go as planned we can reinstall the server again by dropping the database , creating a fresh empty database and modifying the following from true to false in openfire.xml file in location /opt/openfire/conf

<setup>true</setup>

Test the XMPP Server Installation using Spark client

1.Spark can also be downloaded from the same url as was used to download server . Choose your operating system for download

2.Register a spark client with the server

Screenshot from 2015-09-24 14:41:04

3. after registering the client presence should be indicated in the user summary by online status

Screenshot from 2015-09-25 12:55:13

4.Register another client with the same conf except username and password and perform messaging between them

Screenshot from 2015-09-24 14:45:57

XMPP Java Client

Source Code for a Simple Java Application using Smack4 communicating with XMPP servers


package testxmppsmack;

import java.io.IOException;

import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;

public class JabberSmackAPI {
 
 public static void main(String argsp[]){
 
 XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
 .setServiceName("machine")
 .setUsernameAndPassword("admin", "admin")
 .setCompressionEnabled(false)
 .setHost("127.0.0.1")
 .setPort(5222) 
 .setSecurityMode(SecurityMode.disabled)
/* .setSecurityMode(SecurityMode.required) keep this commented */ 
 .setSendPresence(true) 
 .build();
 
 // Create a connection to the the local XMPP server as defined in config above.
 XMPPTCPConnection con = new XMPPTCPConnection(config);
 
 // Connect to the server code is encapsulated in try/catch block for exception handling
 try {
 con.connect();
 System.out.println("Connected "+con.isConnected());
 } catch (SmackException | IOException | XMPPException e1) {
 // TODO Auto-generated catch block
 e1.printStackTrace();
 }
 
 //Login before performing other tasks like messaging etc 
 try {
 con.login("altanai", "aaa");
 System.out.println("Loggedin "+con.isAuthenticated());
 } catch (XMPPException | SmackException | IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 
 
 // Start a new conversation with another account holder caled altanaibisht ( I created 2 user accounts one with my first name and another with fullname) 
 Chat chat = ChatManager.getInstanceFor(con).createChat("altanaibisht@localhost");
 
 try {
 chat.sendMessage("Did you try out the new code i send you last night ?");
 System.out.println("Chat Send ");
 } catch (NotConnectedException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

 // Disconnect from the server
 con.disconnect();


}
}


Some errors and their resolution while building and running the above code as Java Application are as follows :

1. Cannot instantiate XMPPConnection
Use XMPPTCPConnection instead of XMPPConnection in Smack 4.

2. Caused by: java.lang.ClassNotFoundException: org.xmlpull.v1.XmlPullParserFactory

need to have XPP3 (XML Pull Parser 3) in your classpath. Smack 4 does no longer bundle it (unlike Smack 3).

Download the xpp3 from http://www.extreme.indiana.edu/dist/java-repository/xpp3/distributions/

ref :http://stackoverflow.com/questions/24196588/smack-throws-java-lang-classnotfoundexception-org-xmlpull-v1-xmlpullparserfact

3. Exception in thread “main” java.lang.NoClassDefFoundError: de/measite/minidns/DNSCache

http://mvnrepository.com/artifact/de.measite.minidns/minidns/0.1.3

4.  For the jxmpp-util-cache-0.5.0-alpha2.jar

Install it from http://mvnrepository.com/artifact/org.jxmpp/jxmpp-util-cache/0.5.0-alpha2

5.Exception in thread “main” java.lang.NoClassDefFoundError: org/jxmpp/util/XmppStringUtils

http://mvnrepository.com/artifact/org.jxmpp/jxmpp-core/0.4.1

6. Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/http/conn/ssl/StrictHostnameVerifier

http://www.java2s.com/Code/Jar/a/Downloadapachehttpcomponentshttpclientjar.htm

7.Exception in thread “main” java.lang.NoClassDefFoundError: org/xbill/DNS/Lookup

http://www.java2s.com/Code/Jar/d/Downloaddnsjava211jar.htm

8.org.jivesoftware.smack.SmackException$ConnectionException: The following addresses failed: ‘machine:5222’ failed because java.net.ConnectException: Connection refused

.setHost(“127.0.0.1”)
.setPort(5222)

9. org.jivesoftware.smack.SmackException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

.setSecurityMode(SecurityMode.disabled)

Once the program build and runs succesfully connecting to the XMPP server ( which is running ofcourse ) , open a sapark client and test the application with it.

Screenshot from 2015-09-25 12:44:55

Summary

An alternative to XMPP messaging is the SIP for Instant Messaging and Presence Leveraging Extensions (SIMPLE) based on Session Initiation Protocol (SIP).

References :

1.XMPP.org
https://xmpp.org/

2.Getting started from Igniterealtime.org
https://www.igniterealtime.org/builds/smack/docs/latest/documentation/gettingstarted.html

3.IETF RFCs on XMPP ( 2004 ) –
RFC 3920 http://www.ietf.org/rfc/rfc3920.txt
RFC 3921 http://www.ietf.org/rfc/rfc3921.txt

4. Extensions on XMPP
http://xmpp.org/xmpp-protocols/xmpp-extensions/

5. XMPP API explanation by grepcode
http://grepcode.com/file/repo1.maven.org/maven2/org.igniterealtime.smack/smack-core/4.0.0-rc1/org/jivesoftware/smack/XMPPConnection.java

Wowza RTMP Authentication with Third party Token provider over Tiny Encryption Algorithm (TEA)

this article is focused on  Wowza RTMP Authentication with  Third party Token provider over Tiny Encryption Algorithm (TEA)  and  is a continuation of the previous post about setting up a basic RTMP Authentication module on Wowza Engine above version 4.

The task is divided into 3 parts .

  1. RTMP Encoder Application
  2. Wowza RTMP Auth module
  3. Third party Authentication Server

The component diagram is as follows :

Copy of Publisher App iOS

The detailed explanation of the components are :

1.Wowza RTMP Auth module

The Wowza Server receives a rtmp stream url in the format as :

rtmp://username:pass@wowzaip:1935/Application/stteamname

It considers the username and pass to be user credentials . RTMP auth Module invokes the getPassword() function inside of deployed application class  passing the username as parameter.  The username is then  encrypted using TEA ( Tiny Encryption algorithm)

TEA is a block cipher  which is based on symmetric ( private) key encryption . Input is a 64 bit of plain or cipher text with a 128 bit key resulting in output of cipher or plain text respectively.

The code for encryption  is


TEA.encrypt( username, sharedSecret );

The code to make a connection to third party auth server is


 url = new URL(serverTokenValidatorURL);
 
 URLConnection connection;
 connection = url.openConnection();
 connection.setDoOutput(true);

OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
 out.write("clientid=" + TEA.encrypt( username, sharedSecret ););
 out.close(); 

The sharedsecret is the common key which is with both the Auth server and wowza server . It must be atleast a 16 digit alphanumeric / special character based key . An example of shared secret is abcdefghijklmnop .The value can be stored as property in Application.xml file.

<Property>
<Name>secureTokenSharedSecret</Name>
<Value><![CDATA[abcdefghijklmnop]]></Value>
</Property>

<Property>
<Name>serverTokenValidatorURL</Name>
<Value>http://127.0.0.1:8080/TokenProvider/authentication/token</Value&gt;
</Property>

The values of serverTokenValidatorURL is the third party auth server listening for REST POST request .

The code for receiving the incoming  resulting json data is


	ObjectMapper mapper = new ObjectMapper();
	JsonNode node = mapper.readTree(connection.getInputStream()); 
	node = node.get("publisherToken") ;
	String token = node.asText();
        String token2 =TEA.decrypt(token, sharedSecret);

2.Third party Authentication Server

The 3rd party Auth server stores the passwords for users or performs oauth based authentication . It uses a shared secret key to decrypt the token based on TEA as explained in above section .

The code to decrypt the incoming clientId


TEA.decrypt(id, sharedSecret);

Add own custom logic to check files , databases etc for obtaining the password corresponding to the username as decrypted above.

The code to encrypt the password for the user if exists or send invalid response if non exists is


        try {

            String clientID = TEA.decrypt(id, sharedSecret);
            
            String token= findUserPassword(clientID);
            
             token = TEA.encrypt(token, sharedSecret); 
                        
            return "{\"publisherToken\":\""  + token+ "\"}";
            
        }catch (Exception ex) {

            return "{\"error\":\"Invalid Client\"}";
        }

The final callflow thus becomes :

Copy of Publisher App iOS (1)

Screenshots :

Screenshot_2015-09-16-20-22-37Screenshot_2015-09-17-18-36-23Screenshot_2015-09-16-20-22-42Screenshot_2015-09-16-20-23-30

Wowza Secure URL params Authentication for streams in an application

To secure the publishers for a common application through username -password specific for streamnames , this post is useful . It  uses Module Core Security to prompt back the user for supplying credentials.

The detailed code to check the rtmp query-string for parameters  and performs the checks –  is user is allowed to connect and is user allowed to stream on given streamname is given below .

Initialize the hashmap containing publisher clients and IapplicationInstance

	HashMap <Integer, String> publisherClients =null;
	IApplicationInstance appInstance = null;

On app start initilaize the IapplicationInstance object .

	public void onAppStart(IApplicationInstance appInstance)
	{
		this.appInstance = appInstance;
	}

Onconnect is called called when any publisher tries to connects with media server. At this event collect the username and clientId from the client.
Check if publisherclient contains the userName which client has provided else reject the connection .


	public void onConnect(IClient client, RequestFunction function, AMFDataList params)
	{

		AMFDataObj obj = params.getObject(2);
		AMFData data = obj.get("app");

		if(data.toString().contains("?")){

			 String[] paramlist = data.toString().split();
			 String[] userParam = paramlist[1].split("=");
			 String userName = userParam[1];

			if(this.publisherClients==null){
				this.publisherClients = new HashMap<Integer, String>();
			}

			if(this.publisherClients.get(client.getClientId())==null){
				this.publisherClients.put(client.getClientId(),userName);
			} else {
				client.rejectConnection();
			}
		}
	}

AMFDataItem: class for marshalling data between Wowza Pro server and Flash client.

As the event user starts to publish a stream after sucessful connection Onpublishing function is called . It extracts the stream name from the client ( function extractStreamName() )and checks if user is allowed to stream on the given streamname (function isStreamNotAllowed()) .

	public void publish(IClient client, RequestFunction function, AMFDataList params)
	{
		String streamName = extractStreamName(client, function, params);
		if (isStreamNotAllowed(client, streamName))
		{
			sendClientOnStatusError(client, NetStream.Publish.Denied, "Stream name not allowed for the logged in user: "+streamName);
			client.rejectConnection();
		}
		else{
			 invokePrevious(client, function, params);
		}

	}

Function when publisher disconnects from server . It removes the client from publisherClients.

	public void onDisconnect(IClient client)
	{
		if(this.publisherClients!=null){
			this.publisherClients.remove(client.getClientId());
		}
	}

The function to extract a streamname is


public String extractStreamName(IClient client, RequestFunction function, AMFDataList params)
{
String streamName = params.getString(PARAM1);
if (streamName != null)
{
String streamExt = MediaStream.BASE_STREAM_EXT;

String[] streamDecode = ModuleUtils.decodeStreamExtension(streamName, streamExt);
streamName = streamDecode[0];
streamExt = streamDecode[1];
}

return streamName;
}

The fucntion to check if streamname is allowed for the given user


public boolean isStreamNotAllowed(IClient client, String streamName)
{
WMSProperties localWMSProperties = client.getAppInstance().getProperties();
String allowedStreamName = localWMSProperties.getPropertyStr(this.publisherClients.get(client.getClientId()));
String sName="";
if(streamName.contains("?"))
sName = streamName.substring(0, streamName.lastIndexOf(&amp;amp;quot;?&amp;amp;quot;));
else
sName = streamName;
return !sName.toLowerCase().equals(allowedStreamName.toLowerCase().toString()) ;
}

On adding the application to wowza server make sure that the ModuleCoreSecurity is present under Modules in Application.xml

<Module>
<Name>ModuleCoreSecurity</Name>
<Description>Core Security Module for Applications</Description>
<Class>com.wowza.wms.security.ModuleCoreSecurity</Class>
</Module>

Also ensure that property securityPublishRequirePassword is present under properties

<Property>
<Name>securityPublishRequirePassword</Name>
<Value>true</Value>
<Type>Boolean</Type>
</Property>

Add the user credentials as properties too. For example to give access to testuser with password 123456 to stream on myStream include the following ,

<Property>
<Name>testUser</Name>
<Value>myStream</Value>
<Type>String</Type>
</Property>

Also include the mapping of user and password inside of conf/publish.password file

# Publish password file (format [username][space][password])
#username password

testuser 123456

Wowza RTMP Authenticate Module

To purpose of the article is the use the RTMP Authentication Module in wowza Engine .  This will enable us to intercept a connect request with username and password to be checked from any outside source like – database , password file , third party token provider , third party oauth etc.  Once the password provided by user is verified with the authentic password form external sources the user is allowed to connect and publish.

Step 1 : Create a new Wowza Media Server Project in Eclipse .  It is assumed that user has already integrated WowzaIDE into eclipse .

File -> New -> Wowza Media Server Project  

Step 2: Give any project name . I named it as “RTMPAuthSampleCode”.

wowza RTMP Auth

wowza RTMP Auth

Step 3 :   Point the location to existing Wowza Engine installed in local environment .

It is usually in /usr/local/WowzaStreamingEngine/

Wowza RTMP Auth

Wowza RTMP Auth

Step 4 : Proceed with the creation , uncheck the event methods as we are not using them right now .

Screenshot from 2015-09-17 13:10:24

Step 5: Put the code in class.

The class RTMPAuthSampleCode extends AuthenticateUsernamePasswordProviderBase . Its mandatory to define getPassword(String username ) and userExists(String username).  ModuleRTMPAuthenticate will invoke getPassword for connection request from users .

Screenshot from 2015-09-17 13:11:58

We can add any source of obtaining password for a given username which will be matched to the password supplied by user . If it matches he will be granted access otherwise we can return null or error message .

We may use various ways of obtaining user credentials like databse , password files , third part token provider etc . I will be discussing more ways to do RTMP authenticate esp using a third part token provider which using TEA.encrypt and shared secret in the next blog.

Step 6: Build the project and Run.

Project-> Build the Project 

Run -> Run Configurations … -> WowzaMediaServer_RTMPAuthSampleCode

To modules in my ubuntu 64 bit   version 14.04 system , I also need to provide

-Dcom.wowza.wms.native.base=”linux” inside of the VM Arguments . Its highlighted in figure below.

Screenshot from 2015-09-17 13:12:23

Step 7: Click Run to start the wowza Media Engine

Step 8 : Open the Manager Console of Wowza.

web based GUI interface of managing the application and checking for incoming streams . The manager script can be started with

sudo ./usr/local/WowzaStreamingEngine/manager/bin/startmgr.sh

The console can be opened at http://127.0.0.1:8088

Screenshot from 2015-09-17 13:53:58

Also you can see that RTMPAuthSampleCode.jar would have been copied to /usr/local/WowzaStreamingEngine/lib folder.

Step 9: Add module to applications

Add folder “RTMPAuthSampleCode” inside /usr/local/WowzaStreamingEngine/applications folder .

Step 10 : Add conf

Add folder “RTMPAuthSampleCode” inside /usr/local/WowzaStreamingEngine/conf  folder

Copy paste Application.xml from conf folder inside RTMPAuthSampleCode folder and make the following changes .

Add the ModuleRTMPAuthenticate module to Modules

<Module> <Name>ModuleRTMPAuthenticate</Name> <Description>ModuleRTMPAuthenticate</Description> <Class>com.wowza.wms.security.ModuleRTMPAuthenticate</Class> </Module>

and comment ModuleCoreSecurity

<!--    <Module>
     <Name>ModuleCoreSecurity</Name>
     <Description>Core Security Module for Applications</Description>
     <Class>com.wowza.wms.security.ModuleCoreSecurity</Class>
</Module> -->

Step 11: Add property usernamePasswordProviderClass to Properties .

usualy present inside Application at the bootom of Application.xml file

<Property>
<Name>usernamePasswordProviderClass</Name>
<Value>com.wowza.wms.example.authenticate.RTMPAuthSampleCode</Value>
</Property>

Step 12 : Make Authentication.xml file inside /usr/local/WowzaStreamingEngine/conf folder.

Note that from wowza 4 and later versions the Authentiocation.xml has come bundled with wms-server.jar which is inside of lib folder .   However for me , without giving a explicit Authentication.xml file the program froze and using my own simple authentication.xml gave problems with the digest . Hence follow the below process to get a working Authentication.xml file inside conf folder

Expand the archive and  inside the extracted folder wms-server copy the file from location wms-server/com/wowza/wms/conf/Authentication.xml to /usr/local/WowzaStreamingEngine/conf.

Step 13 : Restart Wowza Media Engine .

Step 14 : Use any RTMP encoder as Adobe Live Media Encoder or Gocoder or your own app ( could not use this with ffmpeg ) and  try to connect to application RTMPAuthSampleCode with username test and password 1234.

Step 15 : Observer the logs for incoming streams and traces from getpassword  .

 If you want the user test to have permission to publish stream to this application then return 1234 from getPassword else return null .

References :

  1. Media security overview
    http://www.wowza.com/forums/content.php?115-MediaSecurity-AddOn-Package-(SecureToken-RTMP-RTSP-Authentication-and-more
  2. How to integrate Wowza user authentication with external authentication systems (ModuleRTMPAuthenticate)
    http://www.wowza.com/forums/content.php?236-How-to-integrate-Wowza-user-authentication-with-external-authentication-systems-%28ModuleRTMPAuthenticate%29
  3. How to enable username/password authentication for RTMP and RTSP publishing
    http://www.wowza.com/forums/content.php?449-How-to-enable-username-password-authentication-for-RTMP-and-RTSP-publishing
  4. configuration ref 4.2 http://www.wowza.com/resources/WowzaStreamingEngine_ConfigurationReference.pdf