separate common

This commit is contained in:
MaxKey
2021-02-15 09:57:52 +08:00
parent d1d8924b60
commit 647d899771
221 changed files with 5038 additions and 203 deletions

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Class-Path:

View File

@@ -0,0 +1,153 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp;
import org.maxkey.crypto.password.otp.token.AbstractOtpTokenStore;
import org.maxkey.crypto.password.otp.token.InMemoryOtpTokenStore;
import org.maxkey.domain.UserInfo;
import org.maxkey.util.StringGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractOTPAuthn.
* @author Administrator
*
*/
public abstract class AbstractOtpAuthn {
private static final Logger logger = LoggerFactory.getLogger(AbstractOtpAuthn.class);
protected AbstractOtpTokenStore optTokenStore = new InMemoryOtpTokenStore();
//验证码有效間隔
protected int interval = 30;
// 验证码长度范围410默认为6
protected int digits = 6;
protected String crypto = "HmacSHA1";
StringGenerator stringGenerator;
protected String optType = OptTypes.TIMEBASED_OPT;
public static final class OptTypes {
// 手机
public static String MOBILE = "MOBILE";
// 短信
public static String SMS = "SMS";
// 邮箱
public static String EMAIL = "EMAIL";
//TIMEBASED_OPT
public static String TIMEBASED_OPT = "TOPT";
// HmacOTP
public static String HOTP_OPT = "HOTP";
public static String RSA_OPT = "RSA";
public static String CAP_OPT = "CAP";
}
public abstract boolean produce(UserInfo userInfo);
public abstract boolean validate(UserInfo userInfo, String token);
protected String defaultProduce(UserInfo userInfo) {
return genToken(userInfo);
}
/**
* genToken.
* @param userInfo UserInfo
* @return
*/
public String genToken(UserInfo userInfo) {
if (stringGenerator == null) {
stringGenerator = new StringGenerator(StringGenerator.DEFAULT_CODE_NUMBER, digits);
}
String token = stringGenerator.randomGenerate();
logger.debug("Generator token " + token);
return token;
}
/**
* the interval.
* @return the interval
*/
public int getInterval() {
return interval;
}
/**
* interval the interval to set.
* @param interval the interval to set
*/
public void setInterval(int interval) {
this.interval = interval;
}
/**
* digits.
* @return the digits
*/
public int getDigits() {
return digits;
}
/**
* digits the digits to set.
* @param digits the digits to set
*/
public void setDigits(int digits) {
this.digits = digits;
}
/**
* crypto.
* @return the crypto
*/
public String getCrypto() {
return crypto;
}
/**
* crypto the crypto to set.
* @param crypto the crypto to set
*/
public void setCrypto(String crypto) {
this.crypto = crypto;
}
public String getOptType() {
return optType;
}
public void setOptType(String optType) {
this.optType = optType;
}
public void setOptTokenStore(AbstractOtpTokenStore optTokenStore) {
this.optTokenStore = optTokenStore;
}
public void initPropertys() {
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp;
import java.io.Serializable;
public class OneTimePassword implements Serializable {
private static final long serialVersionUID = -1637133296702014021L;
private String id;
private String type;
private String token;
private String username;
private String receiver;
private String createTime;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OneTimePassword [id=");
builder.append(id);
builder.append(", type=");
builder.append(type);
builder.append(", token=");
builder.append(token);
builder.append(", username=");
builder.append(username);
builder.append(", receiver=");
builder.append(receiver);
builder.append(", createTime=");
builder.append(createTime);
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.algorithm;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* IETF RFC 4226 http://tools.ietf.org/html/rfc4226 HOTP Include HmacOTP's
* implement same as HmacOTP's, when addChecksum = false & truncationOffset = -1
*
* @author Crystal.Sea
*
*/
public class HOTP {
// These are used to calculate the check-sum digits.
// 0 1 2 3 4 5 6 7 8 9
private static final int[] doubleDigits = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
/**
* Calculates the checksum using the credit card algorithm. This algorithm has
* the advantage that it detects any single mistyped digit and any single
* transposition of adjacent digits.
*
* @param num the number to calculate the checksum for
* @param digits number of significant places in the number
*
* @return the checksum of num
*/
public static int calcChecksum(long num, int digits) {
boolean doubleDigit = true;
int total = 0;
while (0 < digits--) {
int digit = (int) (num % 10);
num /= 10;
if (doubleDigit) {
digit = doubleDigits[digit];
}
total += digit;
doubleDigit = !doubleDigit;
}
int result = total % 10;
if (result > 0) {
result = 10 - result;
}
return result;
}
/**
* This method uses the JCE to provide the HMAC-SHA-1 * algorithm. HMAC computes
* a Hashed Message Authentication Code and in this case SHA1 is the hash
* algorithm used.
*
* @param keyBytes the bytes to use for the HMAC-SHA-1 key
* @param text the message or text to be authenticated.
*
* @throws NoSuchAlgorithmException if no provider makes either HmacSHA1 or
* HMAC-SHA-1 digest algorithms available.
* @throws InvalidKeyException The secret provided was not a valid
* HMAC-SHA-1 key.
*
*/
public static byte[] hmac_sha1(byte[]
keyBytes, byte[] text) throws NoSuchAlgorithmException, InvalidKeyException {
// try {
Mac hmacSha1;
try {
hmacSha1 = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException nsae) {
hmacSha1 = Mac.getInstance("HMAC-SHA-1");
}
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmacSha1.init(macKey);
return hmacSha1.doFinal(text);
// } catch (GeneralSecurityException gse) {
// throw new UndeclaredThrowableException(gse);
// }
}
// 0 1 2 3 4 5 6 7 8
private static final int[] DIGITS_POWER
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
/**
* This method generates an OTP value for the given set of parameters.
*
* @param secret the shared secret
* @param movingFactor the counter, time, or other value that changes on a
* per use basis.
* @param codeDigits the number of digits in the OTP, not including the
* checksum, if any.
* @param addChecksum a flag that indicates if a checksum digit
*
*
*
* M'Raihi, et al. Informational [Page 29]
*
* RFC 4226 HOTP Algorithm December 2005
*
*
* should be appended to the OTP.
* @param truncationOffset the offset into the MAC result to begin truncation.
* If this value is out of the range of 0 ... 15, then
* dynamic truncation will be used. Dynamic truncation
* is when the last 4 bits of the last byte of the MAC
* are used to determine the start offset.
* @return A numeric String in base 10 that includes {@link codeDigits} digits
* @throws NoSuchAlgorithmException if no provider makes either HmacSHA1 or
* HMAC-SHA-1 digest algorithms available.
* @throws InvalidKeyException The secret provided was not a valid
* HMAC-SHA-1 key.
*
* plus the optional checksum digit if requested.
*/
public static String generateOTP(byte[]
secret, long movingFactor, int codeDigits, boolean addChecksum,
int truncationOffset) throws NoSuchAlgorithmException, InvalidKeyException {
// put movingFactor value into text byte array
String result = null;
final int digits = addChecksum ? (codeDigits + 1) : codeDigits;
byte[] text = new byte[8];
for (int i = text.length - 1; i >= 0; i--) {
text[i] = (byte) (movingFactor & 0xff);
movingFactor >>= 8;
}
// compute hmac hash
byte[] hash = hmac_sha1(secret, text);
// put selected bytes into result int
int offset = hash[hash.length - 1] & 0xf;
if ((0 <= truncationOffset) && (truncationOffset < (hash.length - 4))) {
offset = truncationOffset;
}
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
int otp = binary % DIGITS_POWER[codeDigits];
if (addChecksum) {
otp = (otp * 10) + calcChecksum(otp, codeDigits);
}
result = Integer.toString(otp);
while (result.length() < digits) {
result = "0" + result;
}
return result;
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.algorithm;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* same as HOTP,but addChecksum = false & truncationOffset = -1.
*
* @author Crystal.Sea
*
*/
public class HmacOTP {
private static final Logger logger = LoggerFactory.getLogger(HmacOTP.class);
/**
* gen.
* @param seed byte
* @param count int
* @param digits int
* @return
*/
public static String gen(byte[] seed, int count, int digits) {
try {
return generateOTP(seed, count, digits);
} catch (InvalidKeyException e) {
e.printStackTrace();
LoggerFactory.getLogger(HmacOTP.class).error(e.getMessage());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
LoggerFactory.getLogger(HmacOTP.class).error(e.getMessage());
}
return "";
}
/**
* @param keyBytes
* @param text
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static byte[] hmac_sha1(byte[] keyBytes, byte[] text)
throws NoSuchAlgorithmException, InvalidKeyException {
Mac hmacSha1;
try {
hmacSha1 = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException nsae) {
hmacSha1 = Mac.getInstance("HMAC-SHA-1");
}
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmacSha1.init(macKey);
return hmacSha1.doFinal(text);
}
/**
* @param secret
* @param movingFactor
* @param codeDigits
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
static private String generateOTP(byte[] secret,
long movingFactor, int codeDigits)
throws NoSuchAlgorithmException, InvalidKeyException {
// put movingFactor value into text byte array
String result = null;
byte[] text = new byte[8];
for (int i = text.length - 1; i >= 0; i--) {
text[i] = (byte) (movingFactor & 0xff);
movingFactor >>= 8;
}
// compute hmac hash
byte[] hash = hmac_sha1(secret, text);
// put selected bytes into result int
int offset = hash[hash.length - 1] & 0xf;
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
int otp = (int) (binary % Math.pow(10, codeDigits));
// int otp = binary % DIGITS_POWER[codeDigits];
result = Integer.toString(otp);
while (result.length() < codeDigits) {
result = "0" + result;
}
return result;
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.algorithm;
public class KeyUriFormat {
public class Types {
public static final String HOTP = "hotp";
public static final String TOTP = "totp";
}
String crypto = "HmacSHA1";
String type;
String secret;
String issuer;
String domain;
int digits = 6;
// just for hotp
Long counter = 0L;
// just for totp
int period = 30;
String account;
public KeyUriFormat() {
}
/**
* @param type
* @param secret
*/
public KeyUriFormat(String type, String secret) {
this.type = type;
this.secret = secret;
}
/**
* @param type
* @param secret
* @param issuer
*/
public KeyUriFormat(String type, String secret, String issuer) {
this.type = type;
this.secret = secret;
this.issuer = issuer;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
/**
* @return the secret
*/
public String getSecret() {
return secret;
}
/**
* @param secret the secret to set
*/
public void setSecret(String secret) {
this.secret = secret;
}
/**
* @return the issuer
*/
public String getIssuer() {
return issuer;
}
/**
* @param issuer the issuer to set
*/
public void setIssuer(String issuer) {
this.issuer = issuer;
}
/**
* @return the digits
*/
public int getDigits() {
return digits;
}
/**
* @param digits the digits to set
*/
public void setDigits(int digits) {
this.digits = digits;
}
/**
* @return the counter
*/
public Long getCounter() {
return counter;
}
/**
* @param counter the counter to set
*/
public void setCounter(Long counter) {
this.counter = counter;
}
/**
* @return the period
*/
public int getPeriod() {
return period;
}
/**
* @param period the period to set
*/
public void setPeriod(int period) {
this.period = period;
}
/**
* @return the account
*/
public String getAccount() {
return account;
}
/**
* @param account the account to set
*/
public void setAccount(String account) {
this.account = account;
}
/**
* @return the crypto
*/
public String getCrypto() {
return crypto;
}
/**
* @param crypto the crypto to set
*/
public void setCrypto(String crypto) {
this.crypto = crypto;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String format() {
return format(this.account);
}
/**
* format account.
* @param account String
* @return
*/
public String format(String account) {
StringBuffer uri = new StringBuffer("otpauth://");
uri.append(type).append("/");
if (null != this.domain) {
uri.append(this.domain).append("/").append(account);
} else {
uri.append(account);
}
uri.append("?secret=").append(secret);
if (null != issuer) {
uri.append("&issuer=").append(issuer);
}
if (digits != 6) {
uri.append("&digits=").append(digits);
}
if (type.equalsIgnoreCase(Types.TOTP) && period != 30) {
uri.append("&period=").append(period);
}
if (type.equalsIgnoreCase(Types.HOTP)) {
uri.append("&counter=").append(counter);
}
return uri.toString();
}
@Override
public String toString() {
return "KeyUriFormat [crypto=" + crypto + ", type=" + type + ", secret=" + secret + ", issuer=" + issuer
+ ", domain=" + domain + ", digits=" + digits + ", counter=" + counter + ", period=" + period
+ ", account=" + account + "]";
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.algorithm;
import java.util.Arrays;
import java.util.Random;
public class OtpSecret {
private static final Random rand = new Random();
/**
* Seed for HMAC-SHA1 - 20 bytes Generates random 20 bytes long TOTP Secret.
*
* @return generated secret
*/
public static final byte[] generate() {
int size = 20;
byte[] b = new byte[size];
rand.nextBytes(b);
return Arrays.copyOf(b, size);
}
/**
* Seed by crypto.
*
* @return generated secret
*/
public static final byte[] generate(String crypto) {
if (crypto.equalsIgnoreCase("HmacSHA1") || crypto.equalsIgnoreCase("HMAC-SHA-1")) {
return generate();
}
if (crypto.equalsIgnoreCase("HmacSHA256") || crypto.equalsIgnoreCase("HMAC-SHA-256")) {
return generate32();
}
if (crypto.equalsIgnoreCase("HmacSHA512") || crypto.equalsIgnoreCase("HMAC-SHA-512")) {
return generate64();
}
return generate();
}
/**
* Seed for HMAC-SHA256 - 32 bytes Generates random 32 bytes long TOTP Secret.
*
* @return generated secret
*/
public static final byte[] generate32() {
int size = 32;
byte[] b = new byte[size];
rand.nextBytes(b);
return Arrays.copyOf(b, size);
}
/**
* Seed forHMAC-SHA512 - 64 bytes Generates random 64 bytes long TOTP Secret.
*
* @return generated secret
*/
public static final byte[] generate64() {
int size = 64;
byte[] b = new byte[size];
rand.nextBytes(b);
return Arrays.copyOf(b, size);
}
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.algorithm;
import java.lang.reflect.UndeclaredThrowableException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class TimeBasedOTP {
// 0 1 2 3 4 5 6 7 8
private static final int[] DIGITS_POWER
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
static String seed64 = "";
static String steps = "0";
static String steps2 = "0";
TimeBasedOTP() {
}
/**
* This method uses the JCE to provide the crypto algorithm. HMAC computes a
* Hashed Message Authentication Code with the crypto hash algorithm as a
* parameter.
*
* @param crypto the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
* @param keyBytes the bytes to use for the HMAC key
* @param text the message or text to be authenticated
*/
private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
try {
Mac hmac;
hmac = Mac.getInstance(crypto);
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
} catch (GeneralSecurityException gse) {
throw new UndeclaredThrowableException(gse);
}
}
/**
* This method converts a HEX string to Byte[].
*
* @param hex the HEX string
*
* @return: a byte array
*/
private static byte[] hexStr2Bytes(String hex) {
// Adding one byte to get the right conversion
// Values starting with "0" can be converted
byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
// Copy all the REAL bytes, not the "first"
byte[] ret = new byte[bArray.length - 1];
for (int i = 0; i < ret.length; i++) {
ret[i] = bArray[i + 1];
}
return ret;
}
/**
* This method generates a OTP value for the given set of parameters. Default
* Crypto HmacSHA512
*
* @param key the shared secret, HEX encoded
* @param time a value that reflects a time
* @param returnDigits number of digits to return
*
* @return: a numeric String in base 10 that includes {@link truncationDigits}
* digits
*/
public static String genOTP(String key, String time, String returnDigits) {
return generateOTP(key, time, returnDigits, "HmacSHA1");
}
/*
* * This method generates a OTP value for the given set of parameters. Crypto
* HmacSHA1
*
* @param key
*
* @param time
*
* @param returnDigits
*
* @return
*/
public static String generateTOTPHmacSHA1(String key, String time, String returnDigits) {
return generateOTP(key, time, returnDigits, "HmacSHA1");
}
/*
* * This method generates a OTP value for the given set of parameters. Crypto
* HmacSHA256
*
* @param key: the shared secret, HEX encoded
*
* @param time: a value that reflects a time
*
* @param returnDigits: number of digits to return
*
* @return: a numeric String in base 10 that includes {@link truncationDigits}
* digits
*/
public static String genOTPHmacSHA256(String key, String time, String returnDigits) {
return generateOTP(key, time, returnDigits, "HmacSHA256");
}
/*
* * This method generates a OTP value for the given set of parameters. Crypto
* HmacSHA256
*
* @param key: the shared secret, HEX encoded
*
* @param time: a value that reflects a time
*
* @param returnDigits: number of digits to return
*
* @return: a numeric String in base 10 that includes {@link truncationDigits}
* digits
*/
public static String genOTPHmacSHA512(String key, String time, String returnDigits) {
return generateOTP(key, time, returnDigits, "HmacSHA512");
}
/**
* This method generates a TOTP value for the given set of parameters.
*/
public static void actualiseTime() {
// double random = Math.random();
long T0 = 0;
long X = 30;
Date d = new Date();
Long k = d.getTime() / 1000;
long testTime[] = { d.getTime() };
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
for (int i = 0; i < testTime.length; i++) {
long T = (k - T0) / X;
long T2 = ((k - T0) / X) - 1;
// System.out.println(T);
steps = Long.toHexString(T).toUpperCase();
steps2 = Long.toHexString(T2).toUpperCase();
while (steps.length() < 16) {
steps = "0" + steps;
}
//String fmtTime = String.format("%1$-11s", k/1000);
//String utcTime = df.format(new Date(testTime[i]));
}
}
/**
* generateOTP.
* @param key String
* @param time String
* @param returnDigits String
* @param crypto String
* @return
*/
public static String generateOTP(String key, String time, String returnDigits, String crypto) {
int codeDigits = Integer.decode(returnDigits).intValue();
String result = null;
// Using the counter
// First 8 bytes are for the movingFactor
// Compliant with base RFC 4226 (HOTP)
while (time.length() < 16) {
time = "0" + time;
}
// Get the HEX in a Byte[]
byte[] msg = hexStr2Bytes(time);
byte[] k = hexStr2Bytes(key);
byte[] hash = hmac_sha(crypto, k, msg);
// put selected bytes into result int
int offset = hash[hash.length - 1] & 0xf;
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
int otp = binary % DIGITS_POWER[codeDigits];
result = Integer.toString(otp);
while (result.length() < codeDigits) {
result = "0" + result;
}
return result;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.domain.UserInfo;
/**
* Chip Authentication Program EMV stands for Europay, MasterCard and Visa, a
* global standard for inter-operation of integrated circuit cards (IC cards or
* "chip cards") and IC card capable point of sale (POS) terminals and automated
* teller machines (ATMs), for authenticating credit and debit card
* transactions.
*
* @author Crystal.Sea
*
*/
public class CapOtpAuthn extends AbstractOtpAuthn {
public CapOtpAuthn() {
optType = OptTypes.CAP_OPT;
}
@Override
public boolean produce(UserInfo userInfo) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import org.apache.commons.codec.binary.Hex;
import org.maxkey.crypto.Base32Utils;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.crypto.password.otp.algorithm.TimeBasedOTP;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CounterBasedOtpAuthn extends AbstractOtpAuthn {
private static final Logger _logger = LoggerFactory.getLogger(CounterBasedOtpAuthn.class);
public CounterBasedOtpAuthn() {
optType = OptTypes.HOTP_OPT;
}
@Override
public boolean produce(UserInfo userInfo) {
return true;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
_logger.debug("SharedCounter : " + userInfo.getSharedCounter());
byte[] byteSharedSecret = Base32Utils.decode(userInfo.getSharedSecret());
String hexSharedSecret = Hex.encodeHexString(byteSharedSecret);
String counterBasedToken = "";
if (crypto.equalsIgnoreCase("HmacSHA1")) {
counterBasedToken = TimeBasedOTP.genOTP(
hexSharedSecret,
userInfo.getSharedCounter(),
"" + digits
);
} else if (crypto.equalsIgnoreCase("HmacSHA256")) {
counterBasedToken = TimeBasedOTP.genOTPHmacSHA256(
hexSharedSecret,
userInfo.getSharedCounter(),
"" + digits
);
} else if (crypto.equalsIgnoreCase("HmacSHA512")) {
counterBasedToken = TimeBasedOTP.genOTPHmacSHA512(
hexSharedSecret,
userInfo.getSharedCounter(),
"" + digits
);
}
_logger.debug("token : " + token);
_logger.debug("counterBasedToken : " + counterBasedToken);
if (token.equalsIgnoreCase(counterBasedToken)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import org.maxkey.crypto.Base32Utils;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.crypto.password.otp.algorithm.HOTP;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HotpOtpAuthn extends AbstractOtpAuthn {
private static final Logger _logger = LoggerFactory.getLogger(HotpOtpAuthn.class);
boolean addChecksum;
int truncation = -1;
public HotpOtpAuthn() {
optType = OptTypes.HOTP_OPT;
}
@Override
public boolean produce(UserInfo userInfo) {
return true;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
_logger.debug("SharedCounter : " + userInfo.getSharedCounter());
byte[] byteSharedSecret = Base32Utils.decode(userInfo.getSharedSecret());
String hotpToken;
try {
hotpToken = HOTP.generateOTP(
byteSharedSecret,
Long.parseLong(userInfo.getSharedCounter()),
digits,
addChecksum, truncation
);
_logger.debug("token : " + token);
_logger.debug("hotpToken : " + hotpToken);
if (token.equalsIgnoreCase(hotpToken)) {
return true;
}
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return false;
}
/**
* the addChecksum.
*/
public boolean isAddChecksum() {
return addChecksum;
}
/**
* addChecksum the addChecksum to set.
*/
public void setAddChecksum(boolean addChecksum) {
this.addChecksum = addChecksum;
}
/**
* the truncation.
*/
public int getTruncation() {
return truncation;
}
/**
* truncation the truncation to set.
*/
public void setTruncation(int truncation) {
this.truncation = truncation;
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import java.text.MessageFormat;
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.SimpleEmail;
import org.maxkey.configuration.EmailConfig;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class MailOtpAuthn extends AbstractOtpAuthn {
private static final Logger _logger = LoggerFactory.getLogger(MailOtpAuthn.class);
@Autowired
EmailConfig emailConfig;
String subject = "One Time PassWord";
String messageTemplate = "{0} You Token is {1} , it validity in {2} minutes.";
public MailOtpAuthn() {
optType = OptTypes.EMAIL;
}
@Override
public boolean produce(UserInfo userInfo) {
try {
String token = this.genToken(userInfo);
Email email = new SimpleEmail();
email.setHostName(emailConfig.getSmtpHost());
email.setSmtpPort(emailConfig.getPort());
email.setSSLOnConnect(emailConfig.isSsl());
email.setAuthenticator(
new DefaultAuthenticator(emailConfig.getUsername(), emailConfig.getPassword()));
email.setFrom(emailConfig.getSender());
email.setSubject(subject);
email.setMsg(
MessageFormat.format(
messageTemplate,userInfo.getUsername(),token,(interval / 60)));
email.addTo(userInfo.getEmail());
email.send();
_logger.debug(
"token " + token + " send to user " + userInfo.getUsername()
+ ", email " + userInfo.getEmail());
//成功返回
this.optTokenStore.store(
userInfo,
token,
userInfo.getMobile(),
OptTypes.EMAIL);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
return this.optTokenStore.validate(userInfo, token, OptTypes.EMAIL, interval);
}
public void setEmailConfig(EmailConfig emailConfig) {
this.emailConfig = emailConfig;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getMessageTemplate() {
return messageTemplate;
}
public void setMessageTemplate(String messageTemplate) {
this.messageTemplate = messageTemplate;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.domain.UserInfo;
public class MobileOtpAuthn extends AbstractOtpAuthn {
public MobileOtpAuthn() {
optType = OptTypes.SMS;
}
@Override
public boolean produce(UserInfo userInfo) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.domain.UserInfo;
/**
* Chip Authentication Program EMV stands for Europay, MasterCard and Visa, a
* global standard for inter-operation of integrated circuit cards (IC cards or
* "chip cards") and IC card capable point of sale (POS) terminals and automated
* teller machines (ATMs), for authenticating credit and debit card
* transactions.
*
* @author Crystal.Sea
*
*/
public class RsaOtpAuthn extends AbstractOtpAuthn {
public RsaOtpAuthn() {
optType = OptTypes.RSA_OPT;
}
@Override
public boolean produce(UserInfo userInfo) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import java.io.IOException;
import java.util.Properties;
import org.maxkey.constants.ConstantsProperties;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class SmsOtpAuthn extends AbstractOtpAuthn {
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthn.class);
protected Properties properties;
@Override
public boolean produce(UserInfo userInfo) {
String token = this.genToken(userInfo);
// TODO:You must add send sms code here
logger.debug("send sms code" + token);
return true;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
return true;
}
protected void loadProperties() throws IOException {
Resource resource = new ClassPathResource(
ConstantsProperties.classPathResource(
ConstantsProperties.classPathResource(
ConstantsProperties.maxKeyPropertySource)));
properties = new Properties();
properties.load(resource.getInputStream());
}
public void initPropertys() {
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.apache.commons.codec.binary.Hex;
import org.maxkey.crypto.Base32Utils;
import org.maxkey.crypto.password.otp.AbstractOtpAuthn;
import org.maxkey.crypto.password.otp.algorithm.TimeBasedOTP;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TimeBasedOtpAuthn extends AbstractOtpAuthn {
private static final Logger _logger = LoggerFactory.getLogger(TimeBasedOtpAuthn.class);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public TimeBasedOtpAuthn() {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@Override
public boolean produce(UserInfo userInfo) {
return true;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
_logger.debug("utcTime : " + dateFormat.format(new Date()));
long currentTimeSeconds = System.currentTimeMillis() / 1000;
byte[] byteSharedSecret = Base32Utils.decode(userInfo.getSharedSecret());
String hexSharedSecret = Hex.encodeHexString(byteSharedSecret);
String timeBasedToken = "";
if (crypto.equalsIgnoreCase("HmacSHA1")) {
timeBasedToken = TimeBasedOTP.genOTP(
hexSharedSecret,
Long.toHexString(currentTimeSeconds / interval).toUpperCase() + "",
digits + "");
} else if (crypto.equalsIgnoreCase("HmacSHA256")) {
timeBasedToken = TimeBasedOTP.genOTPHmacSHA256(
hexSharedSecret,
Long.toHexString(currentTimeSeconds / interval).toUpperCase() + "",
digits + "");
} else if (crypto.equalsIgnoreCase("HmacSHA512")) {
timeBasedToken = TimeBasedOTP.genOTPHmacSHA512(
hexSharedSecret,
Long.toHexString(currentTimeSeconds / interval).toUpperCase() + "",
digits + "");
}
_logger.debug("token : " + token);
_logger.debug("timeBasedToken : " + timeBasedToken);
if (token.equalsIgnoreCase(timeBasedToken)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl.sms;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import java.io.IOException;
import org.maxkey.crypto.password.otp.impl.SmsOtpAuthn;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 阿里云短信验证.
* @author shimingxy
*
*/
public class SmsOtpAuthnAliyun extends SmsOtpAuthn {
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnAliyun.class);
public SmsOtpAuthnAliyun() {
optType = OptTypes.SMS;
}
//请替换你在管理后台应用下申请的accessKeyId
private String accessKeyId = "94395d754eb55693043f5d6a2b772ef3";
//请替换你在管理后台应用下申请的accessSecret
private String accessSecret = "05d5485357bc";
// 短信模板ID
private String templateCode = "SMS_187590021";
private String signName = "MaxKey";
@Override
public boolean produce(UserInfo userInfo) {
// 手机号
String mobile = userInfo.getMobile();
if (mobile != null && !mobile.equals("")) {
try {
DefaultProfile profile = DefaultProfile.getProfile(
"cn-hangzhou", accessKeyId, accessSecret);
IAcsClient client = new DefaultAcsClient(profile);
String token = this.genToken(userInfo);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", mobile);
request.putQueryParameter("SignName", signName);
request.putQueryParameter("TemplateCode", templateCode);
request.putQueryParameter("TemplateParam", "{\"code\":\"" + token + "\"}");
CommonResponse response = client.getCommonResponse(request);
logger.debug("responseString " + response.getData());
//成功返回
if (response.getData().indexOf("OK") > -1) {
this.optTokenStore.store(
userInfo,
token,
userInfo.getMobile(),
OptTypes.SMS);
return true;
}
} catch (Exception e) {
logger.error(" produce code error ", e);
}
}
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
return this.optTokenStore.validate(userInfo, token, OptTypes.SMS, interval);
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessSecret() {
return accessSecret;
}
public void setAccessSecret(String accessSecret) {
this.accessSecret = accessSecret;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
@Override
public void initPropertys() {
try {
this.loadProperties();
} catch (IOException e) {
e.printStackTrace();
}
this.accessKeyId = this.properties.getProperty("config.otp.sms.aliyun.accesskeyid");
this.accessSecret = this.properties.getProperty("config.otp.sms.aliyun.accesssecret");
this.templateCode = this.properties.getProperty("config.otp.sms.aliyun.templatecode");
this.signName = this.properties.getProperty("config.otp.sms.aliyun.signname");
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl.sms;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import java.io.IOException;
import org.maxkey.crypto.password.otp.impl.SmsOtpAuthn;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 腾讯云短信验证.
* @author shimingxy
*
*/
public class SmsOtpAuthnTencentCloud extends SmsOtpAuthn {
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnTencentCloud.class);
//
String secretId;
//
String secretKey;
//短信SDKAPPID
String smsSdkAppid;
//短信模板
String templateId;
//签名
String sign;
public SmsOtpAuthnTencentCloud() {
optType = OptTypes.SMS;
}
@Override
public boolean produce(UserInfo userInfo) {
// 手机号
String mobile = userInfo.getMobile();
if (mobile != null && !mobile.equals("")) {
try {
Credential cred = new Credential(secretId, secretKey);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("sms.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
SmsClient client = new SmsClient(cred, "ap-beijing", clientProfile);
String token = this.genToken(userInfo);
String params = "{\"PhoneNumberSet\":[\"" + mobile + "\"],"
+ "\"TemplateID\":\"" + templateId + "\",\"Sign\":\"" + sign + "\","
+ "\"TemplateParamSet\":[\"" + token + "\",\"" + this.interval + "\"],"
+ "\"SmsSdkAppid\":\"" + smsSdkAppid + "\"}";
SendSmsRequest req = SendSmsRequest.fromJsonString(params, SendSmsRequest.class);
SendSmsResponse resp = client.SendSms(req);
logger.debug("responseString " + SendSmsRequest.toJsonString(resp));
if (resp.getSendStatusSet()[0].getCode().equalsIgnoreCase("Ok")) {
this.optTokenStore.store(
userInfo,
token,
userInfo.getMobile(),
OptTypes.SMS);
return true;
}
} catch (Exception e) {
logger.error(" produce code error ", e);
}
}
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
return this.optTokenStore.validate(userInfo, token, OptTypes.SMS, interval);
}
public String getSecretId() {
return secretId;
}
public void setSecretId(String secretId) {
this.secretId = secretId;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getSmsSdkAppid() {
return smsSdkAppid;
}
public void setSmsSdkAppid(String smsSdkAppid) {
this.smsSdkAppid = smsSdkAppid;
}
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
@Override
public void initPropertys() {
try {
this.loadProperties();
} catch (IOException e) {
e.printStackTrace();
}
this.secretId = this.properties.getProperty("config.otp.sms.tencentcloud.secretid");
this.secretKey = this.properties.getProperty("config.otp.sms.tencentcloud.secretkey");
this.smsSdkAppid = this.properties.getProperty("config.otp.sms.tencentcloud.smssdkappid");
this.templateId = this.properties.getProperty("config.otp.sms.tencentcloud.templateid");
this.sign = this.properties.getProperty("config.otp.sms.tencentcloud.sign");
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl.sms;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.maxkey.crypto.password.otp.impl.SmsOtpAuthn;
import org.maxkey.domain.UserInfo;
import org.maxkey.util.JsonUtils;
import org.maxkey.util.StringGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 网易云信短信验证.
* @author shimingxy
*
*/
public class SmsOtpAuthnYunxin extends SmsOtpAuthn {
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnYunxin.class);
public SmsOtpAuthnYunxin() {
optType = OptTypes.SMS;
}
//发送验证码的请求路径URL
private static final String
SERVER_URL = "https://api.netease.im/sms/sendcode.action";
//网易云信分配的账号请替换你在管理后台应用下申请的Appkey
private String
appKey = "94395d754eb55693043f5d6a2b772ef3";
//网易云信分配的密钥请替换你在管理后台应用下申请的appSecret
private String appSecret = "05d5485357bc";
// 短信模板ID
private String templateId = "14860099";
@Override
public boolean produce(UserInfo userInfo) {
HttpPost httpPost = null;
// 手机号
String mobile = userInfo.getMobile();
if (mobile != null && !mobile.equals("")) {
try {
httpPost = new HttpPost(SERVER_URL);
String curTime = String.valueOf((new Date()).getTime() / 1000L);
/*
* 参考计算CheckSum的java代码在上述文档的参数列表中有CheckSum的计算文档示例
*/
// 随机数
String nonce = new StringGenerator(
StringGenerator.DEFAULT_CODE_NUMBER,
6
).randomGenerate();
String checkSum = SmsOtpAuthnYunxinCheckSumBuilder
.getCheckSum(appSecret, nonce, curTime);
// 设置请求的header
httpPost.addHeader("AppKey", appKey);
httpPost.addHeader("Nonce", nonce);
httpPost.addHeader("CurTime", curTime);
httpPost.addHeader("CheckSum", checkSum);
httpPost.addHeader("Content-Type",
"application/x-www-form-urlencoded;charset=utf-8");
// 设置请求的的参数requestBody参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
/*
* 1.如果是模板短信请注意参数mobile是有s的详细参数配置请参考“发送模板短信文档”
* 2.参数格式是jsonArray的格式例如 "['13888888888','13666666666']"
* 3.params是根据你模板里面有几个参数那里面的参数也是jsonArray格式
*/
//https://api.netease.im/sms/sendcode.action
nvps.add(new BasicNameValuePair("templateid", templateId));
nvps.add(new BasicNameValuePair("mobile", userInfo.getMobile()));
nvps.add(new BasicNameValuePair("codeLen", digits + ""));
//authCode 用户自定义验证码
//nvps.add(new BasicNameValuePair("authCode", ""));
//https://api.netease.im/sms/verifycode.action
//nvps.add(new BasicNameValuePair("code", "123456"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
HttpClient httpClient = HttpClientBuilder.create().build();
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
/*
* 1.打印执行结果打印结果一般会200、315、403、404、413、414、500
* 2.具体的code有问题的可以参考官网的Code状态表
*/
//{"code":200,"msg":"1","obj":"740673"}
String responseString = EntityUtils.toString(response.getEntity(), "utf-8");
//String responseString = "{\"code\":200,\"msg\":\"1\",\"obj\":\"740673\"}";
logger.debug("responseString " + responseString);
YunxinSms yunxinSms =
JsonUtils.gson2Object(responseString,YunxinSms.class);
logger.debug("responseEntity code " + yunxinSms.getObj());
this.optTokenStore.store(
userInfo,
yunxinSms.getObj(),
userInfo.getMobile(),
OptTypes.SMS);
return true;
} catch (Exception e) {
logger.error(" produce code error ", e);
} finally {
if (httpPost != null) {
httpPost.releaseConnection();
}
}
}
return false;
}
@Override
public boolean validate(UserInfo userInfo, String token) {
return this.optTokenStore.validate(userInfo, token, OptTypes.SMS, interval);
}
public String getAppKey() {
return appKey;
}
public void setAppKey(String appKey) {
this.appKey = appKey;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
public class YunxinSms {
int code;
String msg;
String obj;
public YunxinSms() {
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getObj() {
return obj;
}
public void setObj(String obj) {
this.obj = obj;
}
}
@Override
public void initPropertys() {
try {
this.loadProperties();
} catch (IOException e) {
e.printStackTrace();
}
this.appKey = this.properties.getProperty("config.otp.sms.yunxin.appkey");
this.appSecret = this.properties.getProperty("config.otp.sms.yunxin.appsecret");
this.templateId = this.properties.getProperty("config.otp.sms.yunxin.templateid");
}
/**
* main.
* @param args String
* @throws Exception throws
*/
public static void main(String[] args) throws Exception {
String nonce = new StringGenerator(
StringGenerator.DEFAULT_CODE_NUMBER,
6
).randomGenerate();
System.out.println(nonce);
String mapJson = "{\"code\":200,\"msg\":\"1\",\"obj\":\"740673\"}";
YunxinSms yunxinSms = JsonUtils.gson2Object(mapJson,YunxinSms.class);
System.out.println("code " + yunxinSms.getObj());
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.impl.sms;
import java.security.MessageDigest;
public class SmsOtpAuthnYunxinCheckSumBuilder {
// 计算并获取CheckSum
public static String getCheckSum(String appSecret, String nonce, String curTime) {
return encode("sha1", appSecret + nonce + curTime);
}
// 计算并获取md5值
public static String getMD5(String requestBody) {
return encode("md5", requestBody);
}
private static String encode(String algorithm, String value) {
if (value == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
messageDigest.update(value.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
private static final char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd','e', 'f' };
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.token;
import org.maxkey.domain.UserInfo;
public abstract class AbstractOtpTokenStore {
public abstract void store(UserInfo userInfo, String token, String receiver, String type);
public abstract boolean validate(UserInfo userInfo, String token, String type,int interval);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.token;
import org.ehcache.UserManagedCache;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.UserManagedCacheBuilder;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.format.DateTimeFormat;
import org.maxkey.constants.ConstantsTimeInterval;
import org.maxkey.crypto.password.otp.OneTimePassword;
import org.maxkey.domain.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InMemoryOtpTokenStore extends AbstractOtpTokenStore {
private static final Logger logger = LoggerFactory.getLogger(InMemoryOtpTokenStore.class);
protected static final UserManagedCache<String, OneTimePassword> optTokenStore =
UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, OneTimePassword.class)
.withExpiry(
ExpiryPolicyBuilder.timeToLiveExpiration(
java.time.Duration.ofMinutes(ConstantsTimeInterval.ONE_MINUTE * 5)
)
)
.build(true);
@Override
public void store(UserInfo userInfo, String token, String receiver, String type) {
DateTime currentDateTime = new DateTime();
OneTimePassword otp = new OneTimePassword();
otp.setId(userInfo.getUsername() + "_" + type + "_" + token);
otp.setType(type);
otp.setUsername(userInfo.getUsername());
otp.setToken(token);
otp.setReceiver(receiver);
otp.setCreateTime(currentDateTime.toString("yyyy-MM-dd HH:mm:ss"));
optTokenStore.put(otp.getId(), otp);
}
@Override
public boolean validate(UserInfo userInfo, String token, String type, int interval) {
OneTimePassword otp = optTokenStore.get(userInfo.getUsername() + "_" + type + "_" + token);
if (otp != null) {
DateTime currentdateTime = new DateTime();
DateTime oneCreateTime = DateTime.parse(otp.getCreateTime(),
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
Duration duration = new Duration(oneCreateTime, currentdateTime);
int intDuration = Integer.parseInt(duration.getStandardSeconds() + "");
logger.debug("validate duration " + intDuration);
logger.debug("validate result " + (intDuration <= interval));
if (intDuration <= interval) {
return true;
}
}
return false;
}
public InMemoryOtpTokenStore() {
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.maxkey.crypto.password.otp.token;
import org.joda.time.DateTime;
import org.maxkey.constants.ConstantsTimeInterval;
import org.maxkey.crypto.password.otp.OneTimePassword;
import org.maxkey.domain.UserInfo;
import org.maxkey.persistence.redis.RedisConnection;
import org.maxkey.persistence.redis.RedisConnectionFactory;
public class RedisOtpTokenStore extends AbstractOtpTokenStore {
protected int validitySeconds = ConstantsTimeInterval.ONE_MINUTE * 5;
RedisConnectionFactory connectionFactory;
public RedisOtpTokenStore(RedisConnectionFactory connectionFactory) {
super();
this.connectionFactory = connectionFactory;
}
public static String PREFIX = "REDIS_OTP_SERVICE_";
@Override
public void store(UserInfo userInfo, String token, String receiver, String type) {
DateTime currentDateTime = new DateTime();
OneTimePassword otp = new OneTimePassword();
otp.setId(userInfo.getUsername() + "_" + type + "_" + token);
otp.setType(type);
otp.setUsername(userInfo.getUsername());
otp.setToken(token);
otp.setReceiver(receiver);
otp.setCreateTime(currentDateTime.toString("yyyy-MM-dd HH:mm:ss"));
RedisConnection conn = connectionFactory.getConnection();
conn.setexObject(PREFIX + otp.getId(), validitySeconds, otp);
conn.close();
}
@Override
public boolean validate(UserInfo userInfo, String token, String type, int interval) {
RedisConnection conn = connectionFactory.getConnection();
OneTimePassword otp = (OneTimePassword)conn.getObject(
PREFIX + userInfo.getUsername() + "_" + type + "_" + token);
conn.delete(PREFIX + userInfo.getUsername() + "_" + type + "_" + token);
conn.close();
if (otp != null) {
return true;
}
return false;
}
}