jchambers / java-otp Goto Github PK
View Code? Open in Web Editor NEWA one-time password (HOTP/TOTP) library for Java
License: MIT License
A one-time password (HOTP/TOTP) library for Java
License: MIT License
Can you add a module-info.java or make this project an automatic module? I can't use it in my modular application otherwise.
If you allocate a buffer locally in generateOneTimePassword()
rather than as a field, then generateOneTimePassword()
doesn't have to be synchronized. This will improve throughput in multi-threaded situations.
The following code as is from test ExampleApp.java returns error:
Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer;
at com.eatthepath.otp.HmacOneTimePasswordGenerator.generateOneTimePassword(HmacOneTimePasswordGenerator.java:140)
at com.eatthepath.otp.TimeBasedOneTimePasswordGenerator.generateOneTimePassword(TimeBasedOneTimePasswordGenerator.java:143)
at com.eatthepath.otp.TimeBasedOneTimePasswordGenerator.generateOneTimePasswordString(TimeBasedOneTimePasswordGenerator.java:175)
at com.eatthepath.otp.TimeBasedOneTimePasswordGenerator.generateOneTimePasswordString(TimeBasedOneTimePasswordGenerator.java:160)
at simple.ExampleApp.main(ExampleApp.java:30)
package simple;
import javax.crypto.KeyGenerator;
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
public class ExampleApp {
public static void main(final String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator();
final Key key;
{
final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());
// Key length should match the length of the HMAC output (160 bits for SHA-1, 256 bits
// for SHA-256, and 512 bits for SHA-512).
keyGenerator.init(160);
key = keyGenerator.generateKey();
}
final Instant now = Instant.now();
final Instant later = now.plus(totp.getTimeStep());
System.out.println("Current password: " + totp.generateOneTimePasswordString(key, now));
System.out.println("Future password: " + totp.generateOneTimePasswordString(key, later));
}
}
How to validate the generated OTP ?
Hello.
Recently I have this java.lang.NoSuchMethodError
on:
at com.eatthepath.otp.HmacOneTimePasswordGenerator.generateOneTimePassword(HmacOneTimePasswordGenerator.java:140)
at com.eatthepath.otp.TimeBasedOneTimePasswordGenerator.generateOneTimePassword(TimeBasedOneTimePasswordGenerator.java:143)
I guess this is something related to the latest Java 1.8 updated version. Currently I am using:
openjdk version "**1.8.0_312**"
OpenJDK Runtime Environment (Temurin)(**build 1.8.0_312-b07**)
OpenJDK 64-Bit Server VM (Temurin)(build 25.312-b07, mixed mode)
Downloaded on https://adoptium.net/?variant=openjdk8 .
Curiously this wasn't happening on previous Java 1.8 update version.
I am trying to get more information about it and will update this issue as soon as I have, however could you please take a look on this? Perhaps we have more people facing same issue.
This is the version I am using, which I believe is the latest one:
<dependency>
<groupId>com.eatthepath</groupId>
<artifactId>java-otp</artifactId>
<version>0.3.0</version>
</dependency>
Thanks in advance.
I've got my secret key as a string. How can I use thid code without creating a Key object ?
Hello,
according to your README.md Release 0.1 is no longer supported but it is the only release available.
Do you plan release a new version anytime soon or should I vendor/submodule/subtree your repo?
Thanks
I have small question. Because the return type is int, the leading 0's are being dropped. This is being an issue for in the long run as the otp that is being shown is only 5 digits.
Now, can the return type be an String so that the user does not worry about the leading 0's. Personally, we had to waste a lot of time only to come to this conculsion.
Hello,
Given that TOTP needs a shared secret string, how will you generate the code using your library starting from a string? I am planning to embed your library in an app where the user will have another TOTP compliant app to generate the user code that will be compared against the TOTP created by your library. In your example usage you start with a random key.
Any chance you could create an example that starts with a string as a shared secret?
thanks
The documentation of the Mac
class requires that every Java platform supports the following algorithms:
What do you think about adding static factory methods (or changing the existing constructors) to catch NoSuchAlgorithmException
for these algorithms and wrap them inside some unchecked exceptions? Since these algorithms will always be available (unless the JVM is incorrectly configured?) the user currently has to deal with exceptions which can never occur.
HI ,
I have generated the otp using java-otp.jar file , trying to validate the generated otp code using otp.verify ( which is part otp-java.3.2.1) . Below is the sample code.
Note: I am using java7
I have a server which uses TOTP builder and built with java 8 .
I have a client build with java 7 , To get the access , i have used "TimeBasedOneTimePasswordGenerator".
Is it right approach to use the otp code generated with generateOneTimePassword() and validate with totp.verify(TOTP.Builder(x).build and then with totp.verify()?
Could you please help me , what is the wrong in the below code
import com.bastiaanjansen.otp.HMACAlgorithm;
import com.bastiaanjansen.otp.TOTP;
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.UndeclaredThrowableException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.Duration;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.binary.Base32;
import static com.eatthepath.otp.TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA512;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Checktotp {
// Coverting the Secret Key to String
public static String convertSecretKeyToString(SecretKey secretKey) throws NoSuchAlgorithmException {
byte[] rawData = secretKey.getEncoded();
String encodedKey = Base64.getEncoder().encodeToString(rawData);
System.out.println("ALG USed" + secretKey.getAlgorithm());
System.out.println("Format is " + secretKey.getFormat());
return encodedKey;
}
// Converting the String to Secret Key
public static SecretKey convertStringToSecretKeyto(String encodedKey) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey); //encodedKey.getBytes();
SecretKey originalKey = new SecretKeySpec(encodedKey.getBytes(), 0,decodedKey.length ,"AES");
return originalKey;
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, UnsupportedEncodingException {
TOTP totp_latest;
final SecretKey key;
String sec = "RktZUlBCNUQyUU5ERVlRWkY2WE9XQkY3TUU0REw1Q1NZRlZOQkZUQkdaUUVEVUNOU1I1UlpHWlQ3MlFDVDVFUUFJSUZMVzMzR09TRTQ1S1pCVURaUlJTNklLRUI2RFIzV1hFSVJQWT0";
key = convertStringToSecretKeyto(sec);
byte[] secret1 = Base64.getDecoder().decode(sec);
totp_latest = new TOTP.Builder(secret1)
.withPasswordLength(6)
.withAlgorithm(HMACAlgorithm.SHA512)
.withPeriod(Duration.ofSeconds(10))
.build();
// Generating the otp using otp-java-1.3.2
totp_latest.now();
System.out.println("OTP Code with Latest Jar File otp-java-1.3.2 " + totp_latest.now());
// Generating the otp using java-otp-0.1.0 .jar
String code1 = otpNow(sec);
System.out.println("OTP Code with old Jar File java-otp.0.1.0.jar " + code1);
if ( totp_latest.verify(code1)) {
System.out.println("The OTP is matched ");
}
else {
System.out.println("OTP Code is Not Matched");
}
}
public static String otpNow(String key) throws InvalidKeyException, NoSuchAlgorithmException {
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(key);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator(30, SECONDS, 6, TOTP_ALGORITHM_HMAC_SHA512);
final Date now = new Date();
String code = String.valueOf(totp.generateOneTimePassword(originalKey,now ));
return code;
}
}
Here:
the 2nd argument should be the algorithm, but RAW is passed instead.
new TimeBasedOneTimePasswordGenerator() - works ok
new TimeBasedOneTimePasswordGenerator(Duration d) - the constructor is undefined
new TimeBasedOneTimePasswordGenerator(Duration d, int passLength) - the constructor is undefined
new TimeBasedOneTimePasswordGenerator(Duration d, int passLength, String hmacAlgo) - the constructor is undefined
running on v 0.3.0
Java version:
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.22.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.22.04.1, mixed mode, sharing)
Library version: 0.3.1
OS: Ubuntu Linux
I have had issues with the library not being consistent in how long time intervals are handled.
In my use case, I need to have the time interval valid for 15 minutes. In some cases it generates the correct code for the entire 15 minute interval, in other cases the code is only valid for 5 minutes before another code is generated.
Here is the code I am using:
var key = getKey(emailAddress);
var now = Instant.now();
var otp = otpGenerator.generateOneTimePasswordString(key, now);
logger.info(String.format("Time Step is: %d", otpGenerator.getTimeStep().getSeconds()));
logger.info(String.format("Code for 0 seconds: %s", key));
logger.info(String.format("Code for 60 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(60))));
logger.info(String.format("Code for 120 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(120))));
logger.info(String.format("Code for 180 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(180))));
logger.info(String.format("Code for 240 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(240))));
logger.info(String.format("Code for 300 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(300))));
logger.info(String.format("Code for 360 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(360))));
logger.info(String.format("Code for 420 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(420))));
logger.info(String.format("Code for 480 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(480))));
logger.info(String.format("Code for 540 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(540))));
logger.info(String.format("Code for 600 seconds: %s", otpGenerator.generateOneTimePassword(key, now.plusSeconds(600))));
And this is one of the cases where I have called it and it generated a different code. Notice in this case, the message that indicates a new key was created so it isn't the case that this was the same key as a previous instance (and yes, I am aware of the typo in the first print statement that is trying to print the key instead of the code).
(image removed and added in a different comment)
I have the following code setup for testing the library
final Key key;
final KeyGenerator keyGenerator = KeyGenerator.getInstance(TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA512);
keyGenerator.init(512);
key = keyGenerator.generateKey();
TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 6, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA512);
System.out.format("Time: %d Password: %06d\n", Instant.now().getEpochSecond(), totp.generateOneTimePassword(key, Instant.now()));
Thread.sleep(15 * 1000);
System.out.format("Time: %d Password: %06d\n", Instant.now().getEpochSecond(), totp.generateOneTimePassword(key, Instant.now()));
Thread.sleep(14 * 1000);
System.out.format("Time: %d Password: %06d\n", Instant.now().getEpochSecond(), totp.generateOneTimePassword(key, Instant.now()));
Technically all the tree totp codes should be identical right ?
Hi, friend, it would be great if this project was available to use as a dependency on Gradle to use it on Android or the JAR. I made a fork in this project to make that feature possible, but now I do not have much time. However, if you can add that functionality before I have more time available, it would be fabulous. Since I am short of time I will have to copy your classes and add them to my project .
Sometimes generateOneTimePassword returns 5 digits instead of 6. Not sure how to reproduce this
Hi! I catch strange issue, i get from generator zero otp code with current counter 22165.
I attaching a reproducible example with a key.
import com.eatthepath.otp.HmacOneTimePasswordGenerator;
import org.apache.commons.codec.binary.Base32;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.spec.SecretKeySpec;
public class HotpZeroValueTest {
private static final String KEY = "ANGNYSN4D3IZV523MRTXNRJU647E7R64";
@Test
public void test1() throws Exception {
Base32 base32 = new Base32(false);
HmacOneTimePasswordGenerator hotpGenerator = new HmacOneTimePasswordGenerator(6);
SecretKeySpec key = new SecretKeySpec(base32.decode(KEY), HmacOneTimePasswordGenerator.HOTP_HMAC_ALGORITHM);
int otpCode = hotpGenerator.generateOneTimePassword(key, 22164);
Assert.assertEquals(100637, otpCode);
otpCode = hotpGenerator.generateOneTimePassword(key, 22166);
Assert.assertEquals(607204, otpCode);
otpCode = hotpGenerator.generateOneTimePassword(key, 22165);
Assert.assertEquals(0, otpCode);
}
}
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Base64;
public class OTP {
static TimeBasedOneTimePasswordGenerator totp;
static {
try {
totp = new TimeBasedOneTimePasswordGenerator();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
final static Instant now = Instant.now();
final static Instant later = now.plus(totp.getTimeStep());
public OTP() throws NoSuchAlgorithmException {
}
public static String otpNow(String key) throws InvalidKeyException {
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(key);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
Integer code = totp.generateOneTimePassword(originalKey, now);
System.out.println(code);
if(code.toString().length() < 6) {
code = code * 10;
}
return code.toString();
}
public static Integer otpNow(Key key) throws InvalidKeyException {
return totp.generateOneTimePassword(key, now);
}
public static Integer otpLater(Key key) throws InvalidKeyException {
return totp.generateOneTimePassword(key, later);
}
public static String otpLater(String key) throws InvalidKeyException {
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(key);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
Integer code = totp.generateOneTimePassword(originalKey, later);
System.out.println(code);
if(code.toString().length() < 6) {
code = code * 10;
}
return code.toString();
}
public static void main(String[] args) throws InvalidKeyException {
String key = "XX";
System.out.println(OTP.otpNow(key));
}
}
I didn't find any examples how to import secrets. Since I wrote an example which works for me, I wanted to share it.
Here it says that the secret key is encoded with base32
, so the kotlin code looks like following:
val key = SecretKeySpec(Base32().decode("<YOUR-SECRET>"), "RAW")
I hope it's ok to share this. I think the best would be to show this example in README.
As the title says, there are some times when the first digit of the randomly generated One Time Pin is 0. In those cases, Java handles the integer as octal, and the generateOneTimePassword method returns the decimal value of the octal number generated.
For example, if generateOneTimePassword generates 012345 (6 digits), it will return the integer 5349 (4 digits), which is the decimal representation of the octal 012345.
Workaround, as stated in issue #14 :
String oneTimePasswordString = String.format("%06d", oneTimePassword);
I tried to look for this library in Maven, but I cannot find it. It would be super handy to quickly include the library into Maven or Gradle based projects. Any plan to publish it to any Maven repository?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.