How to use WiKID in a JSP application

It is very easy to add WiKID to any application. This document shows how simple it is to add two-factor authentication to jsp-based web application.

This has been tested on Tomcat, but the wAuth protocol can be used to add two-factor authentication to any JSP application, not matter what application server you use.

First, import the WiKID client:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="com.wikidsystems.client.*" %>

This section instantiates the connection between the network client application and the WiKID server. For this to succeed, the network client must have been issued a certificate from the WiKID server. The certificate is contained within a PKCS12 certificate store and requires a passphrase to access.

When the wClient object is instantiated it will load the cert and establish a persistent authenticated SSL connection. This is normally done once per server or application and shared by multiple threads. In this example the object is created and destroyed each page request. This greatly (1000 times) increases overhead of the process but allows all the functions to be shown in this single example page.

Parameters are:

  • wClient(String host, int port, String keyfile, String pass)
  • host = IP address of WIKID server
  • port = TCP port number to connect to (default 8388)
  • keyfile = Path to the PKCS12 certificate file
  • pass = Passphrase to open the PKCS12 file
  • caStore - The certificate authority store for validating the WAS server certificate <-- DO NOT USE Java's cacerts file
  • caStorePass - The passphrase securing the caStore file
<%
	String status="";
      	String chall="";
	wClient wc = new wClient("127.0.0.1", 8388, "Config.getValue("BASEPATH")/private/localhost.p12", "p12_passphrase", Config.getValue("BASEPATH")+"private/caStore", "caStore_passphrase" ) ;

%>

Registration

The registration process associates a device that has resitered it's key with the WiKID server to a userid that represents a individual with rights in the network. Devices can register with the server at will but have no access rights until registered to a userid. Inactive registrations are purged from the system automatically.

The registration process should be completed *only* after validating that the user is not an imposter. This may be done in various ways according to local security policy. It is assumed that whatever validation is required has been completed successfully before callint the registerUsername function.

Parameters are:

  • registerUsername(String user, String regcode, String servercode)
  • user = userid with which to associate device
  • regcode = the registration code provided to the device
  • servercode = the 12-digit code that represents the server/domain

This method returns an integer representing the result of the registration.

<%
	int res = -1;
	if(request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("register")){
          res = wc.registerUsername(request.getParameter("user"), request.getParameter("regcode"), request.getParameter("servercode"));
          if (res==0){
          	status="Success";
          } else {
          	status="Failed ("+res+")";
          }
	}
%>

Login Online

This function is the normal-state login for users. This is called when the users device is connected to the network and able to directly request a passcode for access.

Parameters are:

  • CheckCredentials(String user, String passcode, String servercode)
  • user = userid to validate credentials
  • passcode = time-bounded, 1 use passcode
  • servercode = 12-digit code that represents the server/domain
This method returns a boolean representing sucessful or unsuccessful authentication
<%
	boolean isValid = false;
	if(request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Check Online")){
          isValid = wc.CheckCredentials(request.getParameter("user"), request.getParameter("passcode"), request.getParameter("servercode"));
          if (isValid){
          	status="Success";
          } else {
          	status="Authentication Failed";
          }
	}
%>

Login Offline

This function implements the challenge-reponse authentication for offline devices. Users are given a random challenge and the signed response is returned and validated.

Parameters are:

  • CheckCredentials(String user, String challenge, String response, String servercode)
  • user = userid to validate credentials for
  • challenge = the challeng value provided to the user
  • response = the hashed/signed responss from the device
  • servercode = 12-digit code that represents the server/domain
<%

	if(request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Check Offline")){
          isValid = false;
          isValid = wc.CheckCredentials(request.getParameter("user"), request.getParameter("challenge"), request.getParameter("response"), request.getParameter("servercode"));
          if (isValid){
          	status="Success";
          } else {
         	status="Authentication Failed";
          }
	}
%>

You will also need some code to generate the random challenge for the challenge-response mechanism.

<%
	if(request.getParameter("action")==null){

          	//generate a random number for the offline challenge
        	java.security.SecureRandom sr = java.security.SecureRandom.getInstance("SHA1PRNG");
                long num = sr.nextLong();
                while (num <= 1000000000l){
                  num=sr.nextLong();
                }
                chall = num+"";
                chall = chall.substring(0,8);



            %>

Add additional device to existing userid

This method is used to add an additional device to the users account. It follows the same process as a normal registration but requires a passcode from a device already registered to the userid. This method will authenticate the user with the passcode provided prior to registering the new device.

Parameters are:

  • registerUsername(String user, String regcode, String servercode, String passcode)
  • user = userid with which to associate device
  • regcode = the registration code provided to the device
  • servercode = the 12-digit code that represents the server/domain
  • passcode = time-bounded, 1 use passcode from a device already registered to this user
This method returns an integer representing the result of the registration.
<%

	if(request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Add device")){
          res = -1;
          res = wc.registerUsername(request.getParameter("user"), request.getParameter("regcode"), request.getParameter("servercode"), request.getParameter("passcode"));
          if (res==0){
          	status="Success";
          } else {
          	status="Failed ("+res+")";
          }
	}
%>

Pre-register a user

This method supports user pre-registration.  You may upload a list of userids and    pre-registration codes into the server via the WiKIDAdmin interface.  Users can then    use the pre-registration code provided to them securely by the administrator in    conjunction with the registration code provided by the WiKID token to register in an expidited manner.

Parameters are:

  • preRegister(String preRegistrationCode, String tokenRegistrationCode, String domainCode)
  • preRegistrationCode = code associated with the username that was uploaded to the server
  • tokenRegistrationCode = the registration code provided by the token
  • servercode = the 12-digit code that represents the server/domain

This method returns an integer representing the result of the registration.

    if (request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Pre-register")) {
        res = wc.preRegister(request.getParameter("preregcode"), request.getParameter("tokenregcode"), request.getParameter("servercode"));
        if (res == 0) {
            status = "Success";
        } else {
            status = "Failed (" + res + ")";
        }
    }

Add additional device to existing userid WITHOUT passcode check

This method is used to add an additional device to the users account.  It follows the same process as a normal registration but requires a passcode from a device already registered to the userid.  This method will authenticate the user with the passcode provided prior to registering the new device.The server must have the ALLOW_REG_WITHOUT_PASSCODE parameter devfined and set to True for this call to succeed.

Parameters are:

  • registerUsernameWithoutCheck(String user, String regcode, String servercode)
  • user = userid with which to associate device
  • regcode = the registration code provided to the device
  • servercode = the 12-digit code that represents the server/domain
  • passcode = time-bounded, 1 use passcode from a device already registered to this user

This method returns an integer representing the result of the registration.

    if (request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Add device without passcode")) {
        res = wc.registerUsernameWithoutCheck(request.getParameter("user"), request.getParameter("regcode"), request.getParameter("servercode"));
        if (res == 0) {
            status = "Success";
        } else {
            status = "Failed (" + res + ")";
        }
    }

Find a user by servercode +  userid


This method is used to retrieve a user object from the server.  The network client certificate that was used to establish the wClient connection must be authorized to perform this action.
Parameters are:

  • findUser(String servercode, String userID)
  • servercode = the 12-digit code that represents the server/domain
  • userID = userid of the user to retrieve

This method returns a com.wikidsystems.data.User representing the user on the WAS.

    if (request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Find User")) {
        User user = wc.findUser(request.getParameter("servercode"), request.getParameter("user"));
        if (user != null) {
            session.setAttribute("wikidUser", user);
            StringBuffer sb = new StringBuffer(user.getUserID());
            sb.append(" / ").append(user.getCreation())
                    .append(" / ").append(user.getStatus());
            status = sb.toString();
        } else {
            status = "Failed (No user returned)";
        }

    }

Update the previously "found" user

This method is used to update a user object on the server.  The network client certificate that was used to establish the wClient connection must be authorized to perform this action.
Parameters are:

  • updateUser(User updatedUser)
  • updatedUser = com.wikidsystems.data.User object returned from findUser()

This method returns an integer representing the result of the update on the WAS.

    if (request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Update User")) {
        User user = (User) session.getAttribute("wikidUser");
        user.setBads(Integer.parseInt(request.getParameter("bads")));
        user.setStatus(Integer.parseInt(request.getParameter("status")));
        Iterator it = user.getTokens().iterator();
        while (it.hasNext()) {
            Token t = (Token) it.next();
            long did = t.getDeviceID();
            if (request.getParameter(did + "_offs") != null)
                t.setOffs(Integer.parseInt(request.getParameter(did + "_offs")));
            if (request.getParameter(did + "_bads") != null)
                t.setBads(Integer.parseInt(request.getParameter(did + "_bads")));
            if (request.getParameter(did + "_status") != null)
                t.setStatus(Integer.parseInt(request.getParameter(did + "_status")));
            if (request.getParameter(did + "_delete") != null) t.setForDeletion(true);
        }
        status = "Result: " + wc.updateUser(user);
        session.removeAttribute("wikidUser");
    }

Delete the previously "found" user


This method is used to delete a user object on the server.  The network client certificate that was used to establish the wClient connection must be authorized to perform this action.
Parameters are:

  • deleteUser(User updatedUser)
  • deletedUser = com.wikidsystems.data.User object returned from findUser()

This method returns an integer representing the result of the update on the WAS.

    if (request.getParameter("action") != null && request.getParameter("action").equalsIgnoreCase("Delete User")) {
        User user = (User) session.getAttribute("wikidUser");
        status = "Result: " + wc.deleteUser(user);

    }

Implementing in HTML

And here is the HTML for the page:

<h1>
    <%=status%>
</h1>

<%
    //	if(request.getParameter("action")==null || request.getParameter("action").equalsIgnoreCase("Find User")){

    //generate a random number for the offline challenge
    java.security.SecureRandom sr = java.security.SecureRandom.getInstance("SHA1PRNG");
    long num = sr.nextLong();
    while (num <= 1000000000l) {
        num = sr.nextLong();
    }
    chall = num + "";
    chall = chall.substring(0, 8);


%>
<h1>
    This page demonstrates the general usage of the wClient component.
</h1>

<!-- Registration -->
<hr/>
<h2>Registration:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Registration code: <input type="text" size="12" name="regcode" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Register"><br/>
</form>

<!-- Online Login -->
<hr/>
<h2>Online Login:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Passcode: <input type="text" size="12" name="passcode" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Check Online"><br/>
</form>

<!-- Offline Login -->
<hr/>
<h2>Offline Login:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Challenge: <%= chall %> <input type="hidden" name="challenge" value="<%=chall%>"/><br/>
    Response: <input type="text" size="12" name="response" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Check Offline"><br/>
</form>

<!-- Add device -->
<hr/>
<h2>Add device:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Registration code: <input type="text" size="12" name="regcode" value=""/><br/>
    Passcode: <input type="text" size="12" name="passcode" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Add device"><br/>
</form>

<!-- Pre-registration -->
<hr/>
<h2>Pre-registration:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    Token Registration code: <input type="text" size="12" name="tokenregcode" value=""/><br/>
    Pre-registration code: <input type="text" size="25" name="preregcode" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>
    <p/>
    <input type="submit" name="action" value="Pre-register"><br/>
</form>

//ifdef enterprise-spi
<!-- Add device without passcode-->
<hr/>
<h2>Add device without passcode:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Registration code: <input type="text" size="12" name="regcode" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Add device without passcode"><br/>
</form>
<!-- TX no Preamble -->

<hr/>
<h2>Test bad transaction:
</h2>

<form action="./example.jsp" method="POST">
    <input type="submit" name="action" value="Test No Preamble"><br/>
</form>


<!-- Find User By Name -->
<hr/>
<h2>Find User By Name:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <input type="text" size="25" name="user" value=""/><br/>
    Domain code: <input type="text" size="16" name="servercode" value="<%=defaultservercode%>"/>

    <p/>
    <input type="submit" name="action" value="Find User"><br/>
</form>

<% if (session.getAttribute("wikidUser") != null) {
    User user = (User) session.getAttribute("wikidUser");
%>
<!-- Update User -->

<hr/>
<h2>Update User:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <%=user.getUserID()%>@<%=user.getDomainCode()%><br/>
    Bad Passcode Att: <input type="text" size="3" name="bads" value="<%=user.getBads()%>"/><br/>
    Status: <input type="text" size="2" name="status" value="<%=user.getStatus()%>"/><br/>
    <table border=1>
        <tr>
            <td>Device ID</td>
            <td>Offline Auths</td>
            <td>Bad Pin Att</td>
            <td>Status</td>
            <td>Delete</td>
        </tr>
        <%
            Iterator it = user.getTokens().iterator();
            while (it.hasNext()) {
                Token t = (Token) it.next();
        %>
        <tr>
            <td><%=t.getDeviceID()%>
            </td>
            <td><input type="text" size="8" name="<%=t.getDeviceID()%>_offs" value="<%=t.getOffs()%>"/></td>
            <td><input type="text" size="8" name="<%=t.getDeviceID()%>_bads" value="<%=t.getBads()%>"/></td>
            <td><input type="text" size="8" name="<%=t.getDeviceID()%>_status" value="<%=t.getStatus()%>"/></td>
            <td><input type="checkbox" name="<%=t.getDeviceID()%>_delete"/></td>
        </tr>
        <%
            }
        %>
    </table>
    <input type="submit" name="action" value="Update User"><br/>
</form>

<!-- Delete User -->

<hr/>
<h2>Delete User:
</h2>

<form action="./example.jsp" method="POST">
    <br><br>
    UserID: <%=user.getUserID()%>@<%=user.getDomainCode()%><br/>
    <input type="submit" name="action" value="Delete User"><br/>
</form>
<%
    }
%>
//endif enterprise-spi
<%

} else {
%>
<h1>The wClient connection to the server was NOT successfully established </h1>
<%
    }
%>

</body>
</html>

This page is included in the WiKID server in /opt/WiKID/tomcat/webapps/WiKIDAdmin.  You can edit the page and browse to it for testing.

Ever since deploying WiKID, we  have  secured our Production systems from unauthorized access and maintained PCI compliance