From c924408e11496771b20e668edda93ee01b695258 Mon Sep 17 00:00:00 2001
From: Jon Chambers
{@code TimeBasedOneTimePasswordGenerator} instances are thread-safe and may be shared between threads. Note that - * the {@link #generateOneTimePassword(Key, Instant)} method (and its relatives) are {@code synchronized}; in + * the {@link #generateOneTimePassword(SecretKey, Instant)} method (and its relatives) are {@code synchronized}; in * multi-threaded applications that make heavy use of a shared {@code TimeBasedOneTimePasswordGenerator} instance, * synchronization may become a performance bottleneck. In that case, callers may benefit from using one * {@code TimeBasedOneTimePasswordGenerator} instance per thread (for example, with a {@link ThreadLocal}).
@@ -158,7 +158,7 @@ private static Duration validateTimeStep(final Duration timeStep) { * * @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator */ - public int generateOneTimePassword(final Key key, final Instant timestamp) throws InvalidKeyException { + public int generateOneTimePassword(final SecretKey key, final Instant timestamp) throws InvalidKeyException { return this.hotp.generateOneTimePassword(key, getCounterValue(timestamp)); } @@ -175,7 +175,7 @@ public int generateOneTimePassword(final Key key, final Instant timestamp) throw * * @see Locale#getDefault() */ - public String generateOneTimePasswordString(final Key key, final Instant timestamp) throws InvalidKeyException { + public String generateOneTimePasswordString(final SecretKey key, final Instant timestamp) throws InvalidKeyException { return this.generateOneTimePasswordString(key, timestamp, Locale.getDefault()); } @@ -190,7 +190,7 @@ public String generateOneTimePasswordString(final Key key, final Instant timesta * * @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator */ - public String generateOneTimePasswordString(final Key key, final Instant timestamp, final Locale locale) throws InvalidKeyException { + public String generateOneTimePasswordString(final SecretKey key, final Instant timestamp, final Locale locale) throws InvalidKeyException { return this.hotp.formatOneTimePassword(this.generateOneTimePassword(key, timestamp), locale); } @@ -213,7 +213,7 @@ public String generateOneTimePasswordString(final Key key, final Instant timesta * * @see TOTP: Time-Based One-Time Password Algorithm (RFC 6238) - Security Considerations */ - public boolean validateOneTimePassword(final Key key, final Instant timestamp, final String oneTimePassword) throws InvalidKeyException { + public boolean validateOneTimePassword(final SecretKey key, final Instant timestamp, final String oneTimePassword) throws InvalidKeyException { return hotp.validateOneTimePassword(key, getCounterValue(timestamp), oneTimePassword); } @@ -234,7 +234,7 @@ public boolean validateOneTimePassword(final Key key, final Instant timestamp, f * * @see TOTP: Time-Based One-Time Password Algorithm (RFC 6238) - Security Considerations */ - public boolean validateOneTimePassword(final Key key, final Instant timestamp, final int oneTimePassword) throws InvalidKeyException { + public boolean validateOneTimePassword(final SecretKey key, final Instant timestamp, final int oneTimePassword) throws InvalidKeyException { return hotp.validateOneTimePassword(key, getCounterValue(timestamp), oneTimePassword); } diff --git a/src/main/java/overview.html b/src/main/java/overview.html index c1ecef3..bb4a2bd 100644 --- a/src/main/java/overview.html +++ b/src/main/java/overview.html @@ -36,12 +36,12 @@To actually generate time-based one-time passwords, you'll need a secret key and a timestamp. Secure key management is beyond the scope of this document; for the purposes of an example, though, we'll generate a random key:
-final Key secretKey; +final SecretKey key; { final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm()); keyGenerator.init(160); - secretKey = keyGenerator.generateKey(); + key = keyGenerator.generateKey(); }Armed with a secret key, we can deterministically generate one-time passwords for any timestamp:
diff --git a/src/test/java/com/eatthepath/otp/ExampleApp.java b/src/test/java/com/eatthepath/otp/ExampleApp.java index 1c9b8e5..7a9512e 100644 --- a/src/test/java/com/eatthepath/otp/ExampleApp.java +++ b/src/test/java/com/eatthepath/otp/ExampleApp.java @@ -22,8 +22,8 @@ import javax.crypto.KeyGenerator; import javax.crypto.Mac; +import javax.crypto.SecretKey; import java.security.InvalidKeyException; -import java.security.Key; import java.security.NoSuchAlgorithmException; import java.time.Instant; @@ -31,7 +31,7 @@ public class ExampleApp { public static void main(final String[] args) throws NoSuchAlgorithmException, InvalidKeyException { final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator(); - final Key key; + final SecretKey key; { final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm()); diff --git a/src/test/java/com/eatthepath/otp/HmacOneTimePasswordGeneratorTest.java b/src/test/java/com/eatthepath/otp/HmacOneTimePasswordGeneratorTest.java index 0c95b72..4a19bb3 100644 --- a/src/test/java/com/eatthepath/otp/HmacOneTimePasswordGeneratorTest.java +++ b/src/test/java/com/eatthepath/otp/HmacOneTimePasswordGeneratorTest.java @@ -25,10 +25,10 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; -import java.security.Key; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Locale; @@ -41,7 +41,7 @@ class HmacOneTimePasswordGeneratorTest { - private static final Key HOTP_KEY = + private static final SecretKey HOTP_KEY = new SecretKeySpec("12345678901234567890".getBytes(StandardCharsets.US_ASCII), HmacOneTimePasswordGenerator.HOTP_HMAC_ALGORITHM); diff --git a/src/test/java/com/eatthepath/otp/TimeBasedOneTimePasswordGeneratorTest.java b/src/test/java/com/eatthepath/otp/TimeBasedOneTimePasswordGeneratorTest.java index f47887e..e89496a 100644 --- a/src/test/java/com/eatthepath/otp/TimeBasedOneTimePasswordGeneratorTest.java +++ b/src/test/java/com/eatthepath/otp/TimeBasedOneTimePasswordGeneratorTest.java @@ -26,10 +26,10 @@ import org.junit.jupiter.params.provider.MethodSource; import javax.crypto.Mac; +import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; -import java.security.Key; import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; @@ -102,7 +102,7 @@ void generateOneTimePassword(final String algorithm, final byte[] keyBytes, fina new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm); final Instant timestamp = Instant.ofEpochSecond(epochSeconds); - final Key key = new SecretKeySpec(keyBytes, algorithm); + final SecretKey key = new SecretKeySpec(keyBytes, algorithm); assertEquals(expectedOneTimePassword, totp.generateOneTimePassword(key, timestamp)); } @@ -139,7 +139,7 @@ void generateOneTimePasswordString(final String algorithm, final byte[] keyBytes new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm); final Instant timestamp = Instant.ofEpochSecond(epochSeconds); - final Key key = new SecretKeySpec(keyBytes, algorithm); + final SecretKey key = new SecretKeySpec(keyBytes, algorithm); assertEquals(expectedOneTimePassword, totp.generateOneTimePasswordString(key, timestamp)); } @@ -176,7 +176,7 @@ void generateOneTimePasswordStringLocale(final String algorithm, final byte[] ke new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm); final Instant timestamp = Instant.ofEpochSecond(epochSeconds); - final Key key = new SecretKeySpec(keyBytes, algorithm); + final SecretKey key = new SecretKeySpec(keyBytes, algorithm); assertEquals(expectedOneTimePassword, totp.generateOneTimePasswordString(key, timestamp, locale)); } @@ -210,7 +210,7 @@ private static StreamgenerateOneTimePasswordStringLocale() { void validateOneTimePasswordInt() throws InvalidKeyException { final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator(); final Instant timestamp = Instant.now(); - final Key key = + final SecretKey key = new SecretKeySpec(HMAC_SHA1_KEY_BYTES, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1); assertTrue(totp.validateOneTimePassword(key, timestamp, totp.generateOneTimePassword(key, timestamp))); @@ -223,7 +223,7 @@ void validateOneTimePasswordInt() throws InvalidKeyException { @Test void validateOneTimePasswordString() throws InvalidKeyException { final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator(); - final Key key = + final SecretKey key = new SecretKeySpec(HMAC_SHA1_KEY_BYTES, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1); // A timestamp of 1970-01-01T00:18:00Z with a default TOTP generator produces a one-time password of "003784"