A small and easy-to-use one-time password generator library for Java according to RFC 4226 (HOTP) and RFC 6238 (TOTP).

Overview

OTP-Java

Codacy Badge

A small and easy-to-use one-time password generator for Java according to RFC 4226 (HOTP) and RFC 6238 (TOTP).

Table of Contents

Features

The following features are supported:

  1. Generation of secrets
  2. Time-based one-time password (TOTP, RFC 6238) generation based on current time, specific time, OTPAuth URI and more for different HMAC algorithms.
  3. HMAC-based one-time password (HOTP, RFC 4226) generation based on counter and OTPAuth URI.
  4. Verification of one-time passwords
  5. Generation of OTP Auth URI's

Installation

Maven

<dependency>
    <groupId>com.github.bastiaanjansen</groupId>
    <artifactId>otp-java</artifactId>
    <version>1.1.2</version>
</dependency>

Gradle

implementation 'com.github.bastiaanjansen:otp-java:1.0.5'

Scala SBT

libraryDependencies += "com.github.bastiaanjansen" % "otp-java" % "1.0.5"

Apache Ivy

<dependency org="com.github.bastiaanjansen" name="otp-java" rev="1.0.5" />

Or you can download the source from the GitHub releases page.

Usage

HOTP (Counter-based one-time passwords)

Initialization HOTP instance

To create a HOTPGenerator use the HOTPGeneratorBuilder class as follows:

byte[] secret = "VV3KOX7UQJ4KYAKOHMZPPH3US4CJIMH6F3ZKNB5C2OOBQ6V2KIYHM27Q".getBytes();
HOTPGeneratorBuilder builder = new HOTPGeneratorBuilder(secret);
HOTPGenerator hotp = builder.build();

The above builder creates a HOTPGenerator instance with default values for passwordLength = 6 and algorithm = SHA1. Use the builder to change these defaults:

HOTPGeneratorBuilder builder = new HOTPGeneratorBuilder(secret);
builder
  .withPasswordLength(8)
  .withAlgorithm(HMACAlgorithm.SHA256);
HOTPGenerator hotp = builder.build();

When you don't already have a secret, you can let the library generate it:

// To generate a secret with 160 bits
byte[] secret = SecretGenerator.generate();

// To generate a secret with a custom amount of bits
byte[] secret = SecretGenerator.generate(512);

It is also possible to create a HOTPGenerator instance based on an OTPAuth URI. When algorithm or digits are not specified, the default values will be used.

URI uri = new URI("otpauth://hotp/issuer?secret=ABCDEFGHIJKLMNOP&algorithm=SHA1&digits=6&counter=8237");
HOTPGenerator hotp = HOTPGeneratorBuilder.fromOTPAuthURI(uri);

Get information about the generator:

byte[] secret = hotp.getSecret();
int passwordLength = hotp.getPasswordLength(); // 6
HMACAlgorithm algorithm = hotp.getAlgorithm(); // HMACAlgorithm.SHA1

Generation of HOTP code

After creating an instance of the HOTPGenerator class, a code can be generated by using the generate() method:

try {
    int counter = 5;
    String code = hotp.generate(counter);
    
    // To verify a token:
    boolean isValid = hotp.verify(code, counter);
    
    // Or verify with a delay window
    boolean isValid = hotp.verify(code, counter, 2);
} catch (IllegalStateException e) {
    // Handle error
}

TOTP (Time-based one-time passwords)

Initialization TOTP instance

TOTPGenerator can accept more paramaters: passwordLength, period, algorithm and secret. The default values are: passwordLength = 6, period = 30 and algorithm = SHA1.

// Generate a secret (or use your own secret)
byte[] secret = SecretGenerator.generate();

TOTPGeneratorBuilder builder = new TOTPGeneratorBuilder(secret);

builder
    .withPasswordLength(6)
    .withAlgorithm(HMACAlgorithm.SHA1) // SHA256 and SHA512 are also supported
    .withPeriod(30)
    
TOTPGenerator totp = builder.build();

Or create a TOTPGenerator instance from an OTPAuth URI:

URI uri = new URI("otpauth://totp/issuer?secret=ABCDEFGHIJKLMNOP&algorithm=SHA1&digits=6&period=30");
TOTPGenerator totp = TOTPGeneratorBuilder.fromOTPAuthURI(uri);

Get information about the generator:

byte[] secret = totp.getSecret();
int passwordLength = totp.getPasswordLength(); // 6
HMACAlgorithm algorithm = totp.getAlgorithm(); // HMACAlgorithm.SHA1
Duration period = totp.getPeriod(); // Duration.ofSeconds(30)

Generation of TOTP code

After creating an instance of the TOTPGenerator class, a code can be generated by using the generate() method, similarly with the HOTPGenerator class:

try {
    String code = totp.generate();
     
    // To verify a token:
    boolean isValid = totp.verify(code);
} catch (IllegalStateException e) {
    // Handle error
}

The above code will generate a time-based one-time password based on the current time. The API supports, besides the current time, the creation of codes based on timeSince1970 in milliseconds, Date, and Instant:

try {
    // Based on current time
    totp.generate();
    
    // Based on specific date
    totp.generate(new Date());
    
    // Based on milliseconds past 1970
    totp.generate(9238346823);
    
    // Based on an instant
    totp.generate(Instant.now());
} catch (IllegalStateException e) {
    // Handle error
}

Generation of OTPAuth URI's

To easily generate a OTPAuth URI for easy on-boarding, use the getURI() method for both HOTPGenerator and TOTPGenerator. Example for TOTPGenerator:

TOTPGenerator totp = new TOTPGeneratorBuilder(secret).build();

URI uri = totp.getURI("issuer", "account"); // otpauth://totp/issuer:account?period=30&digits=6&secret=SECRET&algorithm=SHA1

Licence

OTP-Java is available under the MIT licence. See the LICENCE for more info.

Stargazers repo roster for @BastiaanJansen/OTP-Java

Comments
  • Validation not works properly if timestep was given in hours or days

    Validation not works properly if timestep was given in hours or days

    Hi, If i give timestep as hours or days by converting to milliseconds, it was not validating properly(For example if i generate otp ar today 8 Am and it was expiring at day after tomorrow 5:30 AM (GMT time)) and also it takes the time zone calculation as well. Request you to update the code in this issue. I have used HMACSHA512 algorithm with 6 digits. Tried with 30 days but its validating upto 20 days only.

    question 
    opened by nishanthvasu 9
  • TOTPGenerator with

    TOTPGenerator with ".withPeriod()" using less than a second parameter?

    If we create a TOTPGenerator with ".withPeriod()" of less than a second (e.g., ".withPeriod(Duration.ofMillis(1))" ), we get an exception:

    java.lang.ArithmeticException: / by zero at com.bastiaanjansen.otp.TOTPGenerator.calculateCounter(TOTPGenerator.java:164) at com.bastiaanjansen.otp.TOTPGenerator.generate(TOTPGenerator.java:49) . .

    The reason we are trying this is that, from our testing, it appears that when the TOTPGenerator .generate() method is called, it will generate the same/identical code if the calls are within that Duration.ofSeconds(), i.e., if the the generator is created with .withPeriod(DurationofSeconds(10)), all calls to .generate(), within a 10 second period, return the same code.

    We are looking at OTP-Java to generate OTP email codes, so that would mean that if we had requests (from different users) for several OTP email codes within a period of time, then the OTP-Java .generate() method would potentially provide us with the same code for several users.

    Am I misinterpreting how this works?

    Thanks, Jim

    bug question 
    opened by ohaya 9
  • Generated secret too long

    Generated secret too long

    hi~

    thank you for your very useful library!

    There are some strange stuff about SecretGenerator Class, it generate to much long bits than expected..

    I think this code is weird.

    byte[] bytes = new byte[bits * 8];

    
        /**
         * Generate an OTP base32 secret
         *
         * @param bits length, this should match the length of the HMAC algorithm type:
         *             SHA1: 160 bits
         *             SHA256: 256 bits
         *             SHA512: 512 bits
         * @return generated secret
         */
        public static byte[] generate(final int bits) {
            if (bits <= 0)
                throw new IllegalArgumentException("Bits must be higher than 0");
    
            byte[] bytes = new byte[bits * 8];
            SecureRandom random = new SecureRandom();
            random.nextBytes(bytes);
    
            Base32 encoder = new Base32();
            return encoder.encode(bytes);
        }
    

    And I have one question about Custom Key. is it okey to generate the combination key with custom seed ( like ID, email etc ) without using SecretGenerator.class even if bit length is matched with algorithm type

    for example, in case SHA1 - [email protected]_MkpBoD5YCtZi/aTS1EdEHqJneNfsw3oG4.... ( 160bit ) in case SHA256 - [email protected]_MkpBoD5YCtZi/aTS1EdEHqJneNfsw3oG4dqhukGy9l .... ( 256bit )

    bug help wanted 
    opened by whsoul 6
  • With the same secret Generates the same OTP code, as long as it does not expire.

    With the same secret Generates the same OTP code, as long as it does not expire.

    Hi @BastiaanJansen

    I have a question, when I send the same secret and I want to generate an OTP code again, it generates the same as long as it is valid.

    Attached capture

    issues-OTP-JAVA

    question 
    opened by rmatute 4
  • Difference between Authy and generated code.

    Difference between Authy and generated code.

    Hi,

    I'm trying to build my own OTP app using your library, and I'm using my current app (Authy) to check if the codes are the same.

    Parsing the OTPAuth URI gives a different code from what Authy returns. I even tried building the TOTP from scratch using the URI content, and trying the different algorithms, but I never had the same code.

    What would be the issue here (if it is an issue) ?

    Thanks.

    opened by aminecmi 1
  • Does not generate same OTP for same secret and time duration

    Does not generate same OTP for same secret and time duration

    Java Version: OpenJDK 15 OS: Ubuntu Linux

    For the application I am working with, I need to be able to generate the same code for a 15 minute interval. However, this library seems to only allow a max time of 5 minutes before it changes the code.

    Here is the code I am using

        @Autowired
        private Cache<String, byte[]> otpKeyCache;
    
        @Override
        public String generate(String username, String emailAddress)
        {
            var otpGenerator = getOtpGenerator(emailAddress);
            var now = Instant.now();
            var otp = otpGenerator.now();
    
            logger.info(String.format("Time Step is: %d", otpGenerator.getPeriod().getSeconds()));
            logger.info(String.format("Code for 0 seconds: %s", otp));
            logger.info(String.format("Code for 60 seconds: %s", otpGenerator.at(now.plusSeconds(60))));
            logger.info(String.format("Code for 120 seconds: %s", otpGenerator.at(now.plusSeconds(120))));
            logger.info(String.format("Code for 180 seconds: %s", otpGenerator.at(now.plusSeconds(180))));
            logger.info(String.format("Code for 240 seconds: %s", otpGenerator.at(now.plusSeconds(240))));
            logger.info(String.format("Code for 300 seconds: %s", otpGenerator.at(now.plusSeconds(300))));
            logger.info(String.format("Code for 360 seconds: %s", otpGenerator.at(now.plusSeconds(360))));
            logger.info(String.format("Code for 420 seconds: %s", otpGenerator.at(now.plusSeconds(420))));
            logger.info(String.format("Code for 480 seconds: %s", otpGenerator.at(now.plusSeconds(480))));
            logger.info(String.format("Code for 540 seconds: %s", otpGenerator.at(now.plusSeconds(540))));
            logger.info(String.format("Code for 600 seconds: %s", otpGenerator.at(now.plusSeconds(600))));
    
            return otp;
        }
    
        private TOTP getOtpGenerator(String emailAddress)
        {
            var secret = getSecret(emailAddress);
    
            return createOtpGenerator(secret);
        }
    
        private byte[] getSecret(String emailAddress)
        {
            var secret = otpKeyCache.getIfPresent(emailAddress);
    
            if (secret == null)
            {
                secret = SecretGenerator.generate();
                otpKeyCache.put(emailAddress, secret);
            }
    
            return secret;
        }
    
        private TOTP createOtpGenerator(byte[] secret)
        {
            var builder = new TOTP.Builder(secret);
    
            return builder.withPasswordLength(6)
                          .withAlgorithm(HMACAlgorithm.SHA512)
                          .withPeriod(Duration.ofMillis(otpDurationMs))
                          .build();
        }
    

    The attached image shows the code in execution. Please note that this is the first time this method is executed, so it is not getting a "cached" secret.

    otp-code-mismatch2

    question 
    opened by davija 1
  • Bump xstream from 1.4.15 to 1.4.18

    Bump xstream from 1.4.15 to 1.4.18

    Bumps xstream from 1.4.15 to 1.4.18.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Android or Desktop?

    Android or Desktop?

    Your library is for android developers or Java desktop applications developers? Cause I want to use it for my Java Swing desktop application development.

    question 
    opened by RachidLeLaborantin 1
  • TOTPGenerator.verify return false with valid generated otp

    TOTPGenerator.verify return false with valid generated otp

    Hi @BastiaanJansen I am running your example: https://github.com/BastiaanJansen/OTP-Java/blob/main/src/test/java/com/bastiaanjansen/otp/ExampleApp.java

    But when verifying the generated otp it returns false.

    Attached capture:

    image

    Java 11.0.10+9 AdoptOpenJDK

    bug 
    opened by ghost 1
  • Add issuer parameter to uri + encode url parameters

    Add issuer parameter to uri + encode url parameters

    The generated URI did not contain the issuer parameter, as recommended per https://github.com/google/google-authenticator/wiki/Key-Uri-Format https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html

    This pull requests add it It also ensures that URL query parameters are encoded propertly

    opened by WardLootens 0
  • Fix typos in 'MIT License'

    Fix typos in 'MIT License'

    Hi,

    I came across this because I use a Maven plugin to determine the licenses used. Because "MIT License' refers to a name I think it should keep the USA spelling.

    Kind regards,

    Wietsejt

    opened by WietseJT 0
Releases(otp-java-1.3.2)
  • otp-java-1.3.2(May 27, 2022)

    What's Changed

    • Add issuer parameter to uri + encode url parameters by @WardLootens in https://github.com/BastiaanJansen/otp-java/pull/58

    New Contributors

    • @WardLootens made their first contribution in https://github.com/BastiaanJansen/otp-java/pull/58

    Full Changelog: https://github.com/BastiaanJansen/otp-java/compare/otp-java-1.3.1...otp-java-1.3.2

    Source code(tar.gz)
    Source code(zip)
  • otp-java-1.3.1(Apr 7, 2022)

    What's Changed

    • Fix typos in 'MIT License' by @Wietsejt in https://github.com/BastiaanJansen/otp-java/pull/56
    • Dependency upgrade by @BastiaanJansen in https://github.com/BastiaanJansen/otp-java/pull/57

    New Contributors

    • @Wietsejt made their first contribution in https://github.com/BastiaanJansen/otp-java/pull/56

    Full Changelog: https://github.com/BastiaanJansen/otp-java/compare/otp-java-1.3.0...otp-java-1.3.1

    Source code(tar.gz)
    Source code(zip)
  • otp-java-1.3.0(Jan 1, 2022)

    What's Changed

    • GitHub Workflows and fixed 'xstream' dependency vulnerability by @BastiaanJansen in https://github.com/BastiaanJansen/otp-java/pull/54

    Full Changelog: https://github.com/BastiaanJansen/otp-java/compare/v1.0.5...otp-java-1.3.0

    Source code(tar.gz)
    Source code(zip)
  • otp-java-1.2.3(Dec 15, 2021)

    What's Changed

    • Fixed minor bugs, added extra error handling and created several new tests by @BastiaanJansen in https://github.com/BastiaanJansen/otp-java/pull/51

    Full Changelog: https://github.com/BastiaanJansen/otp-java/compare/v1.0.5...otp-java-1.2.3

    Source code(tar.gz)
    Source code(zip)
  • otp-java-1.2.2(Aug 4, 2021)

  • otp-java-1.2.1(Aug 2, 2021)

    1. Fixed bug regarding generating TOTP tokens with Instants.
    2. Periods of less than one second are not allowed and will now throw an IllegalArgumentException.
    3. Added new unit-test case.
    4. Minor code improvements.
    Source code(tar.gz)
    Source code(zip)
  • otp-java-1.2.0(Jul 13, 2021)

  • otp-java-1.1.3(Mar 25, 2021)

  • otp-java-1.1.2(Feb 22, 2021)

  • otp-java-1.1.1(Jan 2, 2021)

  • v1.0.1(Dec 26, 2020)

Owner
Bastiaan Jansen
Software Engineering student at Hogeschool Leiden in The Netherlands.
Bastiaan Jansen
Time-Based One-Time Password (RFC 6238) and HMAC-Based One-Time Password (RFC 4226) reference implementations and more.

Crypto Time-Based One-Time Password (RFC 6238) and HMAC-Based One-Time Password (RFC 4226) reference implementations and more. Getting Started TOTP ge

Oliver Yasuna 1 May 12, 2022
This is an android library to represent password strength.

PasswordStrengthView This is an android library to represent password strength. Preview How to use? Add maven to your project gradle file allprojects

null 33 Jan 3, 2022
Password strength estimator

Nbvcxz - Password strength estimator - [] nbvcxz is java library (and standalone console program) which is heavily inspired by the work in zxcvbn. Pas

GoSimple 237 Dec 29, 2022
evilzip lets you create a zip file(with password) that contains files with directory traversal characters in their embedded path.

evilzip logs 20210701 修改权限问题,让解压后的文件默认就有读写执行的权限。 About evilzip lets you create a zip file(with password) that contains files with directory traversal

鸭王 87 Dec 11, 2022
Trino UDFs Plugin to encrypt/decrypt values with a password

trino-encrypt-udfs Example of Trino UDFs Plugin to encrypt and decrypt values with a password. Introduction In Trino you can create new Plugins by imp

Victor Coustenoble 10 Dec 13, 2022
Tink is a multi-language, cross-platform, open source library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.

Tink A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse. Ubuntu

Google 12.9k Jan 3, 2023
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.6k Dec 29, 2022
Repositório do curso Estruturas de Repetição e Arrays com Java. Curso este oferecido pela Digital Innovation one e ministrado por mim.

ESTRUTURAS DE REPETIÇÃO E ARRAYS COM JAVA Resolução dos exercícios propostos: ESTRUTURAS DE REPETIÇÃO E ARRAYS NA LINGUAGEM JAVA. Curso este que tive

Camila Cavalcante 994 Jan 5, 2023
Easily regenerate worlds at a specific time & date you want (SpigotMC plugin)

Restore/reset worlds at specific times without kicking players from the server! No need to go through the hassle of resetting your worlds manually anymore. Plenty of features are already included in the free version!

Kihsomray 11 Sep 23, 2022
Track your study time!

What's the time-tracker? The time-tracker is a nifty little tool to track the time you spent on your courses. Why does the time-tracker exist? This pr

null 8 Jun 29, 2022
Messenger - A Java based project making use of Sockets for communication between the applications running on different JRE

Messenger - A Java based project making use of Sockets for communication between the applications running on different JRE. Multiple clients can connect at the same time and can send messages to each other, they also get the information of status of their friends connected to the server .

Sarthak Aggarwal 1 Jan 2, 2022
Unofficial Clubhouse web app client. For personal use only. It's a personal open-source project and not affiliated with any company.

Purpose of this web app That's a personal project and not affiliated with any company. This is the web client app to make your Club House experience b

Sergei Ovchinnikov 45 Nov 15, 2022
Are you suffering from forgetting to do HoYoLAB check-in? Use this and be free from it!

GADC 가득 Auto Daily Check-in for Genshin Impact Are you suffering from forgetting to do HoYoLAB check-in? Use this and be free from it! 원신 일일 출첵 매일 까먹으

ForestHouse 2 Jul 11, 2022
Removal of JndiLookup in now obsolete Minecraft versions, or versions that still have log4j < 2.10 and is unable to use

NukeJndiLookupFromLog4j Removal of JndiLookup in now obsolete Minecraft versions, or versions that still have log4j < 2.10 and is unable to use -Dlog4

THONK Monarchy 11 Dec 15, 2022
A Vaadin example application that use Firebase Authentication as its user database

Vaadin + Firebase Auth example A trivial example to use Firebase Authentication with a Vaadin application. The app is built based on start.vaadin.com

Matti Tahvonen 3 Mar 9, 2022
Java binding to the Networking and Cryptography (NaCl) library with the awesomeness of libsodium

kalium - Java binding to the Networking and Cryptography (NaCl) library A Java binding to Networking and Cryptography library by Daniel J. Bernstein.

Bruno Oliveira da Silva 206 Oct 5, 2022
A library for bypassing all of Java's security mechanisms, visibility checks, and encapsulation measures via the JNI API

Narcissus: thwart strong encapsulation in JDK 16+ Narcissus is a JNI native code library that provides a small subset of the Java reflection API, whil

ToolFactory 29 Nov 3, 2022
An authorization library that supports access control models like ACL, RBAC, ABAC in Java

jCasbin News: still worry about how to write the correct jCasbin policy? Casbin online editor is coming to help! Try it at: http://casbin.org/editor/

Casbin 2k Dec 30, 2022
A Twitter-API library JAVA

Tweety A Twitter-API library for JAVA. Code for Authorization (Oauth 1) can be found here :Authorization This api conta

Rohit Kumar 2 Apr 26, 2022