What is OTP?
The full name of OTP is One-Time Password, which is often called "one-time password" or "dynamic password" in Chinese. It is a dynamically generated short-term valid password for authentication, often providing additional security when logging in or performing sensitive operations. OTP is widely used in mainstream platforms such as Google, Microsoft, and GitHub to enhance the security of user accounts.
Features of OTP include:
- One-time use: Each password can only be used once and cannot be repeated.
- Timeliness: The password is valid for a short period of time and cannot be used after expiration.
- Dynamic generation: Passwords are generated dynamically based on time or counter.
The principle of OTP generation
There are two common OTP implementation standards:
HOTP(HMAC-Based One-Time Password): Counter-based OTP.
TOTP(Time-Based One-Time Password): Time-based OTP.
TOTP is the most widely used standard at present. It uses the shared secret key and the current time as input, and combines the HMAC-SHA1 algorithm to generate a short digital password. Here are the main steps of TOTP:
Divide the current timestamp by the time step (for example, 30 seconds) to get the time index.
The HMAC algorithm is used to calculate the hash value of the time index.
Extracts the dynamic offset of the hash value and generates a 6-bit or 8-bit digital password.
Implement OTP service in Java
The following is a Java-based OTP service implementation that supports generation and verification of OTP.
Introduce dependencies
On the projectAdd the following dependencies to the file:
<dependencies> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> </dependencies>
Implement OTP generation logic
The following code implements time-based TOTP generation and verification functions:
import ; import ; import .Base32; import ; import ; import ; import ; public class OtpService { private static final int TIME_STEP = 30; // Time step (seconds) private static final int OTP_LENGTH = 6; // OTP length private static final int MAX_ATTEMPTS = 5; // Maximum number of attempts private static final long BLOCK_DURATION = 300_000; // Block time (milliseconds) private final ConcurrentHashMap<String, AtomicInteger> attemptCounter = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Long> blockedUsers = new ConcurrentHashMap<>(); // Generate TOTP public String generateTOTP(String secret) throws Exception { long timeIndex = ().getEpochSecond() / TIME_STEP; return generateOtp(secret, timeIndex); } // Verify TOTP public boolean validateTOTP(String secret, String otp, String userId) throws Exception { if (isBlocked(userId)) { ("User is temporarily blocked: " + userId); return false; } long timeIndex = ().getEpochSecond() / TIME_STEP; // Check OTP in the verification window for (int i = -1; i <= 1; i++) { String generatedOtp = generateOtp(secret, timeIndex + i); if ((otp)) { resetAttempts(userId); return true; } } recordFailedAttempt(userId); return false; } private String generateOtp(String secret, long timeIndex) throws Exception { // Decode Base32 key Base32 base32 = new Base32(); byte[] keyBytes = (secret); // Conversion time index is a byte array byte[] timeBytes = (8).putLong(timeIndex).array(); // Generate hash using HMAC-SHA1 Mac mac = ("HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "HmacSHA1"); (keySpec); byte[] hash = (timeBytes); // Extract dynamic offsets int offset = hash[ - 1] & 0x0F; int binary = ((hash[offset] & 0x7F) << 24) | ((hash[offset + 1] & 0xFF) << 16) | ((hash[offset + 2] & 0xFF) << 8) | (hash[offset + 3] & 0xFF); // Generate OTP int otp = binary % (int) (10, OTP_LENGTH); return ("%0" + OTP_LENGTH + "d", otp); } private void recordFailedAttempt(String userId) { (userId, new AtomicInteger(0)); int attempts = (userId).incrementAndGet(); if (attempts >= MAX_ATTEMPTS) { (userId, ()); ("User blocked due to multiple failed attempts: " + userId); } } private void resetAttempts(String userId) { (userId); (userId); } private boolean isBlocked(String userId) { Long blockTime = (userId); if (blockTime == null) { return false; } if (() - blockTime > BLOCK_DURATION) { (userId); return false; } return true; } }
Testing Services
Here is an example of generating and validating OTP using the above OTP service:
public class OtpServiceTest { public static void main(String[] args) throws Exception { OtpService otpService = new OtpService(); // Use Base32 encoding key String secret = "JBSWY3DPEHPK3PXP"; String userId = "user123"; // Generate OTP String otp = (secret); ("Generated OTP: " + otp); // Try to verify OTP for (int i = 0; i < 7; i++) { boolean isValid = (secret, otp, userId); ("Attempt " + (i + 1) + ": Is OTP valid: " + isValid); } } }
Optimization suggestions
Security:
Use a secure random number generator to generate a shared key.
Transfer data over HTTPS to prevent man-in-the-middle attacks.
Time synchronization:
Time between the client and the server must be synchronized, otherwise OTP verification may fail.
Prevent violent cracking:
Increases the number of failed attempts limit and blocking mechanism.
Production environment:
Securely store shared keys in the database to avoid leakage.
Implement rate limiting to prevent brute-breaking attacks.
This article introduces the basic principles of OTP and implements a simple OTP service through Java, which is compatible with mainstream applications such as Google Authenticator and Microsoft Authenticator. OTP technology provides users with additional authentication security through dynamic passwords, and is one of the most reliable two-factor authentication technologies at present.
This is the end of this article about Java implementing OTP (dynamic password) service. For more related Java OTP dynamic password content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!