forked from fujieid/jap
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
⚡ jap-mfa, Time based one-time password (TOTP)
- Loading branch information
Showing
8 changed files
with
498 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns="http://maven.apache.org/POM/4.0.0" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>jap</artifactId> | ||
<groupId>com.fujieid</groupId> | ||
<version>1.0.0-alpha.1</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>jap-mfa</artifactId> | ||
<name>jap-mfa</name> | ||
<description> | ||
Time based one-time password (TOTP), which is suitable for Google authenticator and TOTP authenticator of binary | ||
boot | ||
</description> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.google.zxing</groupId> | ||
<artifactId>core</artifactId> | ||
<version>3.3.3</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.warrenstrange</groupId> | ||
<artifactId>googleauth</artifactId> | ||
<version>1.4.0</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/* | ||
* Copyright (c) 2020-2040, 北京符节科技有限公司 ([email protected] & https://www.fujieid.com). | ||
* <p> | ||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* http://www.gnu.org/licenses/lgpl.html | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.fujieid.jap.sso; | ||
|
||
import cn.hutool.core.img.ImgUtil; | ||
import cn.hutool.core.io.FileUtil; | ||
import cn.hutool.extra.qrcode.QrCodeUtil; | ||
import cn.hutool.log.Log; | ||
import cn.hutool.log.LogFactory; | ||
import com.warrenstrange.googleauth.*; | ||
|
||
import javax.servlet.http.HttpServletResponse; | ||
import java.awt.*; | ||
import java.io.File; | ||
import java.io.IOException; | ||
|
||
/** | ||
* Jap Multi-Factor Authenticator | ||
* <p> | ||
* Time based one-time password (TOTP) | ||
* <p> | ||
* https://tools.ietf.org/html/rfc6238 | ||
* | ||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) | ||
* @version 1.0.0 | ||
* @since 1.0.0 | ||
*/ | ||
public class JapMfa { | ||
|
||
private static final Log log = LogFactory.get(); | ||
|
||
private final GoogleAuthenticator authenticator; | ||
private final JapMfaConfig mfaConfig; | ||
|
||
public JapMfa(ICredentialRepository credentialRepository, JapMfaConfig mfaConfig) { | ||
this.mfaConfig = mfaConfig; | ||
this.authenticator = new GoogleAuthenticator(new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder() | ||
.setCodeDigits(mfaConfig.getDigits()) | ||
.setTimeStepSizeInMillis(mfaConfig.getPeriod()) | ||
.setHmacHashFunction(HmacHashFunction.valueOf(mfaConfig.getAlgorithm().name())) | ||
.build()); | ||
authenticator.setCredentialRepository(credentialRepository); | ||
} | ||
|
||
public JapMfa(ICredentialRepository credentialRepository) { | ||
this(credentialRepository, new JapMfaConfig()); | ||
} | ||
|
||
public GoogleAuthenticator getAuthenticator() { | ||
return authenticator; | ||
} | ||
|
||
public String getSecretKey(String username) { | ||
return authenticator.getCredentialRepository().getSecretKey(username); | ||
} | ||
|
||
/** | ||
* Returns the URL of totp | ||
* | ||
* @param username The user name | ||
* @param issuer The issuer name. This parameter cannot contain the colon (:) character. | ||
* @return {@code String} | ||
*/ | ||
public String getTotpUrl(String username, String issuer) { | ||
final GoogleAuthenticatorKey key = authenticator.createCredentials(username); | ||
return GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(issuer, username, key); | ||
} | ||
|
||
/** | ||
* Returns the URL of a Google Chart API call to generate a QR barcode to be loaded into the Google Authenticator application. | ||
* <p> | ||
* The user scans this bar code with the application on their smart phones or enters the secret manually. | ||
* | ||
* @param username The user name | ||
* @param issuer The issuer name. This parameter cannot contain the colon (:) character. | ||
* @return {@code String} | ||
*/ | ||
public String getOtpQrCodeUrl(String username, String issuer) { | ||
final GoogleAuthenticatorKey key = authenticator.createCredentials(username); | ||
return GoogleAuthenticatorQRGenerator.getOtpAuthURL(issuer, username, key); | ||
} | ||
|
||
/** | ||
* One time password verification by user name | ||
* | ||
* @param username The user name | ||
* @param otpCode The totp code | ||
* @return {@code bool} | ||
*/ | ||
public boolean verifyByUsername(String username, int otpCode) { | ||
return authenticator.authorizeUser(username, otpCode); | ||
} | ||
|
||
/** | ||
* One time password verification by OTP secret key | ||
* | ||
* @param secret The totp secret Key. | ||
* @param otpCode The totp code | ||
* @return {@code bool} | ||
*/ | ||
public boolean verifyBySecret(String secret, int otpCode) { | ||
return authenticator.authorize(secret, otpCode); | ||
} | ||
|
||
/** | ||
* Create OTP QR code and write it to browser through {@code HttpServletResponse} | ||
* | ||
* @param username The user name | ||
* @param issuer The issuer name. This parameter cannot contain the colon (:) character. | ||
* @param response HttpServletResponse | ||
*/ | ||
public void createOtpQrcode(String username, String issuer, HttpServletResponse response) { | ||
try { | ||
QrCodeUtil.generate(getTotpUrl(username, issuer), | ||
mfaConfig.getQrcodeWidth(), mfaConfig.getQrcodeHeight(), | ||
mfaConfig.getQrcodeImgType(), response.getOutputStream()); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
/** | ||
* Create and return the QR code file of OTP | ||
* | ||
* @param username The user name | ||
* @param issuer The issuer name. This parameter cannot contain the colon (:) character. | ||
*/ | ||
public File getOtpQrcodeFile(String username, String issuer) { | ||
String tempFilePath = mfaConfig.getQrcodeTempPath(); | ||
String tempFileFullPath = tempFilePath.concat(username) | ||
.concat(String.valueOf(System.currentTimeMillis())) | ||
.concat(".") | ||
.concat(mfaConfig.getQrcodeImgType()); | ||
log.debug("Create QR code: {}", tempFileFullPath); | ||
File file = FileUtil.newFile(tempFileFullPath); | ||
if (FileUtil.exist(file)) { | ||
FileUtil.del(file); | ||
} else { | ||
FileUtil.mkParentDirs(file); | ||
} | ||
return QrCodeUtil.generate(getTotpUrl(username, issuer), | ||
mfaConfig.getQrcodeWidth(), mfaConfig.getQrcodeHeight(), file); | ||
} | ||
|
||
/** | ||
* Create and return the base64 string of OTP QR code | ||
* | ||
* @param username The user name | ||
* @param issuer The issuer name. This parameter cannot contain the colon (:) character. | ||
* @param deleteFile Delete temporary QR code file | ||
*/ | ||
public String getOtpQrcodeFileBase64(String username, String issuer, boolean deleteFile) { | ||
File imgFile = this.getOtpQrcodeFile(username, issuer); | ||
Image image = ImgUtil.read(imgFile); | ||
String base64Str = ImgUtil.toBase64DataUri(image, mfaConfig.getQrcodeImgType()); | ||
if (deleteFile) { | ||
FileUtil.del(imgFile); | ||
} | ||
return base64Str; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
jap-mfa/src/main/java/com/fujieid/jap/sso/JapMfaAlgorithm.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.fujieid.jap.sso; | ||
|
||
/** | ||
* The cryptographic hash function used to calculate the HMAC (Hash-based Message Authentication Code). | ||
* <p> | ||
* This implementation uses the SHA1 hash function by default. | ||
* | ||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) | ||
* @version 1.0.0 | ||
* @since 1.0.0 | ||
*/ | ||
public enum JapMfaAlgorithm { | ||
/** | ||
* SHA1 | ||
*/ | ||
HmacSHA1, | ||
/** | ||
* SHA256 | ||
*/ | ||
HmacSHA256, | ||
/** | ||
* SHA512 | ||
*/ | ||
HmacSHA512 | ||
} |
113 changes: 113 additions & 0 deletions
113
jap-mfa/src/main/java/com/fujieid/jap/sso/JapMfaConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Copyright (c) 2020-2040, 北京符节科技有限公司 ([email protected] & https://www.fujieid.com). | ||
* <p> | ||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* http://www.gnu.org/licenses/lgpl.html | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.fujieid.jap.sso; | ||
|
||
import java.io.File; | ||
|
||
/** | ||
* Multi-Factor Authenticator Configuration | ||
* | ||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) | ||
* @version 1.0.0 | ||
* @since 1.0.0 | ||
*/ | ||
public class JapMfaConfig { | ||
/** | ||
* the number of digits in the generated code. | ||
*/ | ||
private int digits = 6; | ||
/** | ||
* the time step size, in milliseconds, as specified by RFC 6238. The default value is 30.000s. | ||
* | ||
* @see <a href="https://tools.ietf.org/html/rfc6238#section-5.2" target="_blank">5.2. Validation and Time-Step Size</a> | ||
*/ | ||
private long period = 30000; | ||
/** | ||
* the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512) | ||
*/ | ||
private JapMfaAlgorithm algorithm = JapMfaAlgorithm.HmacSHA1; | ||
|
||
private String qrcodeTempPath = System.getProperties().getProperty("user.home") + File.separator + "jap" + File.separator + "temp"; | ||
|
||
private int qrcodeWidth = 200; | ||
|
||
private int qrcodeHeight = 200; | ||
|
||
private String qrcodeImgType = "gif"; | ||
|
||
public int getDigits() { | ||
return digits; | ||
} | ||
|
||
public JapMfaConfig setDigits(int digits) { | ||
this.digits = digits; | ||
return this; | ||
} | ||
|
||
public long getPeriod() { | ||
return period; | ||
} | ||
|
||
public JapMfaConfig setPeriod(long period) { | ||
this.period = period; | ||
return this; | ||
} | ||
|
||
public JapMfaAlgorithm getAlgorithm() { | ||
return algorithm; | ||
} | ||
|
||
public JapMfaConfig setAlgorithm(JapMfaAlgorithm algorithm) { | ||
this.algorithm = algorithm; | ||
return this; | ||
} | ||
|
||
public String getQrcodeTempPath() { | ||
return qrcodeTempPath; | ||
} | ||
|
||
public JapMfaConfig setQrcodeTempPath(String qrcodeTempPath) { | ||
this.qrcodeTempPath = qrcodeTempPath; | ||
return this; | ||
} | ||
|
||
public int getQrcodeWidth() { | ||
return qrcodeWidth; | ||
} | ||
|
||
public JapMfaConfig setQrcodeWidth(int qrcodeWidth) { | ||
this.qrcodeWidth = qrcodeWidth; | ||
return this; | ||
} | ||
|
||
public int getQrcodeHeight() { | ||
return qrcodeHeight; | ||
} | ||
|
||
public JapMfaConfig setQrcodeHeight(int qrcodeHeight) { | ||
this.qrcodeHeight = qrcodeHeight; | ||
return this; | ||
} | ||
|
||
public String getQrcodeImgType() { | ||
return qrcodeImgType; | ||
} | ||
|
||
public JapMfaConfig setQrcodeImgType(String qrcodeImgType) { | ||
this.qrcodeImgType = qrcodeImgType; | ||
return this; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
jap-mfa/src/main/java/com/fujieid/jap/sso/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright (c) 2020-2040, 北京符节科技有限公司 ([email protected] & https://www.fujieid.com). | ||
* <p> | ||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* http://www.gnu.org/licenses/lgpl.html | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/** | ||
* Time based one-time password (TOTP), which is suitable for Google authenticator and TOTP authenticator of binary boot | ||
* | ||
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) | ||
* @version 1.0.0 | ||
* @since 1.0.0 | ||
*/ | ||
package com.fujieid.jap.sso; |
Oops, something went wrong.