Skip to content

Commit

Permalink
Adding complete code for OTPManager
Browse files Browse the repository at this point in the history
  • Loading branch information
abdulwaheed18 committed Aug 23, 2016
1 parent 0b5e710 commit 7239400
Show file tree
Hide file tree
Showing 42 changed files with 3,230 additions and 1 deletion.
107 changes: 107 additions & 0 deletions IMPLEMENTATION.md
@@ -0,0 +1,107 @@
# Implementation of OTPManager - One time password library

### Project Integration

Add below dependency to your build environment

<!-- Slf4J logger -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>

<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.0</version>
</dependency>
Or copy all the libraries from OTPManager-1.0.0-archive\lib into your application classpath.

* Add OTPManager-1.0.0-archive/OTPManager-1.0.0.jar' into your pom.xml file or copy it into your application classpath.
* Copy OTPManager-1.0.0-archive/config/otp.properties' file into your configuration directory and change 'ISSUER.NAME' value.

### Implementation
* UserInfo: Need to be created in third-party application and make it persistent either in database file or flat file.
It stores OTP related details details of User. Check UserInfo in OTPManager Javadoc or [example] (https://github.com/GSLabDev/OTPManager/tree/master/example) project.

* IUSerInfoDAO: Must implement and initialize in OTPManager, It required to read/write userInfo object from/to the database. Check example\src\com\gslab\otp\example\UserInfoDAOImpl class for details.

* IEncryptor: OTPManager comes with default AESEncryptor which do encrypt/decrypt the shared secret but for security purpose
it must be implemented by third party Web Application. Check example\src\com\gslab\otp\example\AESEncryptor class for details.

* OtpHandler: OTPManager has init(...) which is designed to be called only once. To handle OTPManager in multi threading environment,
Caller should has to create one handler class which must be double checked singleton and it internally call all OTPManager API’s. Check example\src\com\gslab\otp\example\OtpHandler class for more details.

### Usage

The following code is designed to be called only once in the application lifetime.

OTPManager otpManager = new OTPManager();
otpManager.init(otpFilePath, UserInfoDAOImpl, EncryptorImpl)

Where optFilePath -> Path of otp.properties file
UserInfoDAOImpl -> Implementation of IuserInfoDAO to perform read/write userInfo into the database.
EncryptorImpl -> Implementation of IEncryptor which encrypt/decrypt the shared Secret.

The above code must be called inside the getInstance() of a Singleton class e.g. OtpHandler class as explained above.

private static volatile OtpHandler instance = null;
public static OtpHandler getInstance() throws OTPManagerException {
if (instance == null) { // single checked
synchronized (OtpHandler.class) {
if (instance == null) { // double checked
instance = new OtpHandler();
otpManager = new OTPManager();
// specify the filepath of otp.properties file
otpFile = new File("config", OTP_FILE);
// For storing userInfo detail,Temporary we are using flat file
// rather than database. We HAVE TO implement IUserInfoDAO
// for reading/writing userInfo Entity from database
userInfoDAO = new UserInfoDAOImpl();
// using bydefault Encryptor for encrypting/decryping shared
// secret,Ideally we should implement our Encryptor by
// implementing IEncryptor interface for Encryption
encryptor = new AESEncryptor();

// Should be initialize while starting the application and should
// not be called more than once in whole Application
otpManager.init(otpFile, userInfoDAO, encryptor);
}
}
}
return instance;
}
To enable OTP for a user,It has to register to OTPManager to get the qrCodeURL.

UserInfo userInfo = new UserInfo();
userInfo.setAccountName(“ACCOUNTNAME”);//accountName of user
userInfo.setType(Type.TOTP);// OTP Type
String qrCodeURL = otpHandler.getInstance().register(userInfo);
It returns qrcodeUrl using which caller can generate QRCODE image or you can use OTPManager Utility class to get QRCodeBaseURL which will display Image qrcode directly at runtime.

String qrcodeBaseURL = Utils.getQRCodeAsURL(qrcode, 200, 200);

The following code verifies the OTP code against the QRCode Image.

String boolean = otpHandler.getInstance()..verifyOTP(otpCode, userId)
Please refer to the [examples](https://github.com/GSLabDev/OTPManager/tree/master/example) sub-directory for more details.
28 changes: 28 additions & 0 deletions LICENSE
@@ -0,0 +1,28 @@
OTPManager - One time password library

Copyright (c) 2016, Great Software Laboratory Private Limited.
All rights reserved.

Contributor: Abdul Waheed [abdul.waheed@gslab.com]

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the "Great Software Laboratory Private Limited" nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 changes: 59 additions & 1 deletion README.md
@@ -1 +1,59 @@
# OTPManager
OTPManager - One time password library
==============================================

Copyright (c) 2016, [Great Software Laboratory Private Limited](http://gslab.com/).

Contributor: Abdul Waheed [abdul.waheed@gslab.com]

[https://github.com/GSLabDev/OTPManager](https://github.com/GSLabDev/OTPManager)

**OTPManager** is a pluggable component that implements Time-based One-time Password [TOTP] (https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) algorithm specified in RFC 6238 and HMAC based One-time Password [HOTP] (https://en.wikipedia.org/wiki/HMAC-based_One-time_Password_Algorithm) algorithm specified in RFC 4226. Using this component, a web application can support OTP Authentication without worrying much about the OTP implementation. It easily provide additional layer of security i.e. two factor authentication in any web application.

**OTPManager** is licensed under the Open Source License.

**Client Application**

**OTPManager** will act as Authenticator server whereas Google Authenticator application (available for [iOS] (https://itunes.apple.com/in/app/google-authenticator/id388497605), [Android] (https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2), [BlackBerry] (https://appworld.blackberry.com/webstore/content/29401059) and [Windows] (https://www.microsoft.com/en-us/store/p/authenticatorg/9nblggh082hc) ) as a client to generates OTP code.

**Library Documentation**

This library includes full JavaDoc documentation and an example code to understand implementation of **OTPManager** library in a better way.

# Usage

## Requirements ( build dependencies )
* Java Version 6 or above
* Apache Maven 3 or higher

## Instructions

### Build

mvn clean install

It will create the 'OTPManager-1.0.0-archive.zip' file under OTPManager/target directory which will be having 'OTPManager-1.0.0.jar'
as well as all others dependencies required for this project. It also contains one properties file named otp.properties.

### Usage in code

See [examples](https://github.com/GSLabDev/OTPManager/tree/master/example) for working example.

## Documentation

The API documentation will get generated after mvn install under OTPManager/target/apidocs directory.

For OTPManager library implementation details, see
[IMPLEMENTATION.md](https://github.com/GSLabDev/libasynckafkaclient/blob/master/IMPLEMENTATION.md)


## Examples

See the [examples](https://github.com/GSLabDev/OTPManager/tree/master/example) sub-directory.


## Support

File bug reports, feature requests and questions using
[GitHub Issues](https://github.com/GSLabDev/OTPManager/issues)


15 changes: 15 additions & 0 deletions example/config/otp.properties
@@ -0,0 +1,15 @@
# The initial windowSize used when validating the codes. We are using Google's default behaviour of using a window size equal to 3. The range should be between 1 to 10
TOTP.WINDOWSIZE=3

# look-ahead parameters on the server to make it in sync. The range should be between 1 to 10.
HOTP.WINDOWSIZE = 5

#REQUIRED if TYPE is HOTP, The counter parameter is required when provisioning a key for use with HOTP. It will set the initial counter value.
HOTP.COUNTER=1

#throttling parameter which defines the maximum number of possible attempts for One-Time Password validation.
MAX.OTP.FAILURE =5

#The issuer parameter is a string value indicating the provider or service this account is associated with.
# Valid values corresponding to the label prefix examples above would be: ISSUER.NAME=Example, ISSUER.NAME=Provider1, and ISSUER.NAME=Big%20Corporation.
ISSUER.NAME=OTP_EXAMPLE
Binary file added example/lib/OTPManager-1.0.0.jar
Binary file not shown.
Binary file added example/lib/commons-codec-1.9.jar
Binary file not shown.
Binary file added example/lib/jasypt-1.9.0.jar
Binary file not shown.
Binary file added example/lib/junit-4.8.2.jar
Binary file not shown.
Binary file added example/lib/slf4j-api-1.7.7.jar
Binary file not shown.
Binary file added example/lib/slf4j-simple-1.7.7.jar
Binary file not shown.
92 changes: 92 additions & 0 deletions example/src/com/gslab/otp/example/AESEncryptor.java
@@ -0,0 +1,92 @@
/**
* Copyright 2014 GSLAB. All Rights Reserved.
*/
package com.gslab.otp.example;

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gslab.otp.IEncryptor;
import com.gslab.otp.OTPManagerException;

/**
* The AESEncryptor class implements {@link IEncryptor} interface. This class
* provides the mechanism of Encrypting/Decrypting the Base32 encoded shared
* Secret. {@link OTPManager} uses this class as default for encryption which
* can be override during initialization.
*
* @author abdul.waheed@gslab.com (Abdul Waheed)
* @version 1.0
*
*/
public class AESEncryptor implements IEncryptor {

private StandardPBEStringEncryptor encrytor;

/**
* The logger for this class
*/
private static final Logger LOG = LoggerFactory
.getLogger(AESEncryptor.class);

public AESEncryptor() {
encrytor = new StandardPBEStringEncryptor();
encrytor.setAlgorithm("PBEWithMD5AndDES");

// Ideally the password should not be maintained directly in the
// source code, but rather kept someplace secure
encrytor.setPassword("ZgPiPSCdq88K8Mfay7T7IA");
}

/*
* (non-Javadoc)
*
* @see com.gslab.otp.IEncryptor#encrypt(java.lang.String)
*/
public String encrypt(String sharedSecret) throws OTPManagerException {
LOG.info("Encrypting shared Secret...");
if (sharedSecret == null || sharedSecret.isEmpty()) {
LOG.error("SharedSecret cannot be null or empty");
throw new OTPManagerException(
"SharedSecret cannot be null or empty");
}
String encryptedKey = null;
try {
encryptedKey = encrytor.encrypt(sharedSecret);
} catch (Exception e) {
LOG.error("Error while encrypting sharedSecret {}", e.getMessage(),
e);
throw new OTPManagerException(
"Error while encrypting sharedSecret " + e.getMessage(), e);
}
LOG.debug("Encrypted Shared Secret successfully");
return encryptedKey;
}

/*
* (non-Javadoc)
*
* @see com.gslab.otp.IEncryptor#decrypt(java.lang.String)
*/
public String decrypt(String sharedSecret) throws OTPManagerException {
LOG.info("Decrypting shared Secret...");
String decryptedKey = null;
if (sharedSecret == null || sharedSecret.isEmpty()) {
LOG.error("SharedSecret cannot be null or empty");
throw new OTPManagerException(
"SharedSecret cannot be null or empty");
}
try {
decryptedKey = encrytor.decrypt(sharedSecret);
} catch (Exception e) {
// for Invalid sharedSecret, e.getMessage will be null
LOG.error("Error while decrypting sharedSecret {}", e.getMessage(),
e);
throw new OTPManagerException(
"Error while decrypting sharedSecret " + e.getMessage(), e);
}
LOG.debug("Decrypted Shared Secret successfully");
return decryptedKey;
}
}
52 changes: 52 additions & 0 deletions example/src/com/gslab/otp/example/JAXBUtil.java
@@ -0,0 +1,52 @@
/**
* Copyright 2014 GSLAB. All Rights Reserved.
*/
package com.gslab.otp.example;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
* This is utility class used to do all JAXB serialization to/from disk. Please
* use this whenever you need to read/write all configuration classes from/to
* disk. It also requires that all JAXB serializeable classes are included into
* jaxb.index.
*
* @author abdul.waheed@gslab.com (Abdul Waheed)
*
*/
public class JAXBUtil {

private static JAXBContext context;

private static Marshaller marshaller;
private static Unmarshaller unmarshaller;

private static JAXBContext getContext() throws JAXBException {
if (context == null) {
context = JAXBContext.newInstance(JAXBUtil.class.getPackage()
.getName());
}
return context;

}

public static Marshaller getMarshaller() throws JAXBException {
if (marshaller == null) {
marshaller = getContext().createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
return marshaller;
}

public static Unmarshaller getUnmarshaller() throws JAXBException {
if (unmarshaller == null) {
unmarshaller = getContext().createUnmarshaller();
}
return unmarshaller;

}

}

0 comments on commit 7239400

Please sign in to comment.