Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 6 of 6
The initial implementation of the PassKeySequencerFactory has a notion of what the default PassKeySequencer implementation (that is, the shared sequence algorithm) is, but it can also be directed to create a specific implementation
based on a fully qualified class name.
Leveraging a common factory pattern design leaves all other components oblivious to the details of determining the why, what,
and how of the sequence-algorithm selection. Listing 7 shows a PassKeySequencerFactory class that provides a PassKeySequencer based on the following precedence order:
HashSequencer)
package com.opensolutionspace.otp.lib;
import java.lang.*;
import java.util.*;
import com.opensolutionspace.common.*;
/**
* Provides ease of constructing an implementor of PassKeySequencer ...
* Clients of PassKeySequencer's need not concern themselves with
* selecting a specific sequence algorithm (i.e., implementation),
* the details of creation/initialization and the finite set of implementors.
*
* @author lji
* 02/02/2009
*/
public class PassKeySequencerFactory {
/**
* Create and return a Sequence instance based on the "classpath" value ...
*/
static public PassKeySequencer createSequence(String classPath) {
// Use Hash if what user asks for is NA
PassKeySequencer passKeySequence = new HashSequencer();
//
try{
Class theClass = Class.forName( classPath );
passKeySequence = (PassKeySequencer)theClass.newInstance();
//SimpleLogger.info( "PassKeySequenceFactory: successfully created: " + passKeySequence.getClass().getName() );
}
catch( InstantiationException e ){
SimpleLogger.error( "PassKeySequenceFactory:" + e );
}
catch( IllegalAccessException e ){
SimpleLogger.error( "PassKeySequenceFactory:" + e );
}
catch( ClassCastException e ){
SimpleLogger.error( "PassKeySequenceFactory:" + e );
}
catch( ClassNotFoundException e ){
SimpleLogger.error( "PassKeySequenceFactory:" + e );
}
if( passKeySequence == null ) {
passKeySequence = new HashSequencer();
SimpleLogger.error( "PassKeySequenceFactory: Seq Class Was Not Instantiated - Still null" );
}
return passKeySequence;
}
/**
* Returns the "default/current" Sequence Type
*/
static public PassKeySequencer createSequence() {
String seqType;
ObjectAttributes attributes;
attributes = ObjectAttributes.createObjectAttributes("PassKeySequencerFactory");
attributes.loadAttributes();
seqType = attributes.getString("sequenceType");
//System.out.println("client attributes: " + attributes.toString());
if(seqType == null) {
seqType = PassKeySequencer.DEFAULT_SEQUENCE_TYPE;
}
return createSequence(seqType);
}
}
The implementation of SimpleOtpAuthority and its base class -- BaseOtpAuthority -- is also worth reviewing. This is where all client requests are validated, providing OtpService instances with a single point for all validation, including passkey validation. Instances of PassKeySequencer are not directly used by OtpService implementations. Listing 8 shows the implementation of the two classes.
package com.opensolutionspace.otp.poc;
import java.util.*;
import com.opensolutionspace.otp.lib.*;
/**
*
* 02/01/2009
* Used by the SecureService class
* It's only validation check on the clientId: it cannot be empty
* @author lji
*/
public class SimpleOtpAuthority extends BaseOtpAuthority implements OtpAuthority {
public SimpleOtpAuthority() { }
/**
* Defined as abstract in BaseOtpAuthority
*/
protected boolean validRequester(String id) {
if(id == null || id.length() == 0)
return false;
return true;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
package com.opensolutionspace.otp.lib;
import java.util.Hashtable;
import java.util.Map;
import com.opensolutionspace.common.SimpleLogger;
/**
* 02/03/2009
*
* Convenience base OTP Service Class - carries data structures and
* default key management and authentication methods.
* Concrete classes that implement the OtpService would be well served
* by extending this and modifying/extending behavior as needed.
* @author lji
*
*/
public abstract class BaseOtpAuthority {
private Map<String,String> requesterMap;
private PassKeySequencer passKeySequence;
public BaseOtpAuthority() {
init();
}
protected void init() {
requesterMap = new Hashtable<String,String>(83);
passKeySequence = PassKeySequencerFactory.createSequence();
return;
}
// required to be implemented by derived classes
protected abstract boolean validRequester(String id) ;
protected void update(String id, String password) {
requesterMap.put(id, password);
}
protected void purgeId(String id) {
requesterMap.remove(id);
}
protected boolean knownClient(String id) {
if( requesterMap.containsKey(id) == false) {
return false;
}
return true;
}
protected boolean authenticate(String id, String password) {
/**
* Reject empty s!
*/
if( password == null || password.length() == 0 )
return false;
// perform operation on to obtain what should be stored
// on behalf of this client ...
String real = passKeySequence.encode(password);
// stored value ...
String found = requesterMap.get(id);
if(real.equals(found) == true ) {
update(id, password) ;
return true;
}
return false;
}
////////////////////////////////////////////////////
///// Public methods
/**
* 1st interaction between client and server "conversation"
*/
public void hello(String id, String info ) throws SecureServiceException {
if(this.validRequester(id) == true)
update(id, info);
else
throw new SecureServiceException(
SecureServiceException.Code.AUTHORIZATION_EXCEPTION);
}
/**
* 2 opportunities for failure - 'unknown/untrusted client', or bad password ...
*/
public int authorizeRequest(
String id, String info, String request, String... args ) {
// Are we satisfied with the way the client is identifying himself?
if( knownClient(id) == false) {
SimpleLogger.error("Unknown Client - NOT Allowing " + request + " on behalf of " + id + "\n");
return OtpService.UNKNOWN_CLIENT;
}
// Is the passkey valid?
if( authenticate(id, info ) == false) {
SimpleLogger.error(" PassKey Rejected - NOT Allowing " + request + " on behalf of " + id + "\n");
return OtpService.INVALID_PASSWORD;
}
// Ok, go ahead and allow service
SimpleLogger.info("Allowing " + request + " on behalf of " + id );
return OtpService.OK;
}
public void goodbye(String id, String info ) throws SecureServiceException {
// validate the request to end this client conversation
// Is the password valid
if( authenticate(id, info ) == false) {
SimpleLogger.error("PassKey Rejected - NOT terminating conversation on behalf of " + id + "\n");
throw new SecureServiceException(
SecureServiceException.Code.AUTHORIZATION_EXCEPTION);
}
// Don't even bother checking if 'id' already exists - just purge
purgeId(id);
SimpleLogger.info("Terminating conversation on behalf of " + id + "\n");
}
}
The interfaces and classes in the reference implementation are partitioned across five packages. Here's a brief synopsis of what each package contains, with the packages listed in roughly least-to-most dependent order:
*.otp.test contains The test driver class: TestDriver*.appl contains the original client/service classes: SecureSampleClient and SecureSampleService*.common contains several generally useful "utility classes" that are not specific to any particular application: ObjectAttributes, SimpleLogger, ServiceException,, and Utility.
*.otp.lib contains core interfaces and base-class implementations specific to OTP generation, maintenance, and validation: BaseOtpAuthority, PassKeySequencer, BaseSequencer, PassKeySequencerFactory, HashSequencer, SecureServiceException, OtpAuthority, and OtpService*.otp.poc contains proof-of-concept implementations or class stubs that extend base classes in *.otp.lib: FiboSequencer, SimpleOtpAuthority, StringSequencer, FicaOtpAuthority, and SimpleSequencer. Note: If a developer wanted to utilize this reference implementation, the contents of common, otp.lib, and otp.poc would be provided in one or more JARs.
*.otp.appl contains the client/service classes that rely on OTP -- extensions of the*.appl components SecureSampleClient and SecureSampleService.
Figure 5 shows the package interdependencies.
Leveraging tried and tested algorithms often proves to be the main ingredient in a software solution recipe. Incorporating single-usage, or one-time, passwords into requests passed between cooperating entities is not a novel idea, but it is a practical one. Lamport's approach focuses on synchronizing client/service entities through a common sequencing algorithm used both to generate and to validate passkey values sent along with every interaction. It might not be the only security precaution you take, but it's worth considering between distributed applications if you would like to:
As published, the Lamport algorithm focuses on sequence-value generation and management, within an OTP context. The solution details I've offered in this article (relating to client/service interactions) are beyond its scope. The reference implementation shows how you can apply an OTP framework to an existing client/service collaboration with minimal coding, and it can be extended to generate alternate sequence algorithms and stricter client-identifier validation. However, this is a proof-of-concept-level code -- a baseline. Here are some suggestions for taking this implementation to the next level:
SampleClient and SampleService implementations.
PassKeySequencer implementations.
OtpAuthority implementations.
OtpAuthority implementations should probably "age out" the client-identifier records after a period of idleness and/or fixed period.
Leslie Lamport is credited with first suggesting that using a one-way function to generate a sequence of values could be useful for securing distributed systems through one-time (expiring) passwords. Most of Lamport's career has revolved around solution algorithms for distributed computing.
Special acknowledgments are owed to two trusted peers and friends: Jeff Kroll of HP Software and Tim Ihde of DoubleCheck, Inc. -- Jeff for noticing the practical usefulness of Lamport's algorithm and sharing his quick study with me, and Tim for long ago introducing me to the multifaceted world of application security.
Louis J. Iacona has been designing and developing software since 1982, mainly on UNIX/Linux platforms. Most recently, his efforts have focused on Java/J2EE-implemented solutions for enterprise-scoped applications and leveraging virtualization platforms. Louis is currently on assignment at HP Software in Paramus, New Jersey, and can be reached at louis.iacona@verizon.net.
Read more about Core Java in JavaWorld's Core Java section.