Two Factor Authentication
What is two factor authentication?
two factor means utilizing two factor classes
-
Phone
-
Password
- 指紋
- ...
http://www.slideshare.net/markstanislav/twofactor-authentication-a-primer
Modern two factor authication
-
RSA SecurID
- https://en.wikipedia.org/wiki/RSA_SecurID
-
TOTP
- https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm
特性
-
方便 , Google 有提供totp app
-
時間延遲短 , 比起簡訊來的快太多
-
沒有每次產生費用
-
需有硬體產生器 or 智慧型手機
-
RSA SecurID
-
RSA SecurID
TOTP Authentication
TOTP Authentication
- Time-based One-time Password Algorithm
- Is based on HOTP with a timestamp replacing the incrementing counter.
- https://github.com/google/google-authenticator
- RFC-6238(https://tools.ietf.org/html/rfc6238)
Demo
- http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
- http://totp-authentication-demo.herokuapp.com/
使用原理
- Server產生一組totp綁定碼 給User
- User透過App 紀錄totp綁定碼
- User每次登入時 , 輸入App根據綁定碼產生的totp code
- Server驗證totp code、userId與資料庫根據userId撈出的totp綁定碼.
Code
public class TOTP {
private static final int[] DIGITS_POWER
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
private static byte[] hmacSha(String crypto, byte[] keyBytes, byte[] text) {
try {
Mac hmac = Mac.getInstance(crypto);
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
} catch (GeneralSecurityException gse) {
throw new UndeclaredThrowableException(gse);
}
}
public static int generateTOTP(byte[] key, long time, int digits, String crypto) {
byte[] msg = ByteBuffer.allocate(8).putLong(time).array();
byte[] hash = hmacSha(crypto, key, 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[digits];
return otp;
}
}
Code
public class TOTPUtils {
private int SECRET_SIZE = 10;
private int PASS_CODE_LENGTH = 6;
private int WINDOW = 3;
private int INTERVAL = 30;
private String CRYPTO = "HmacSHA1";
public String generateSecret() {
// Allocating the buffer
byte[] buffer = new byte[SECRET_SIZE];
// Filling the buffer with random numbers.
rand.nextBytes(buffer);
byte[] secretKey = Arrays.copyOf(buffer, SECRET_SIZE);
byte[] encodedKey = BaseEncoding.base32().encode(secretKey).getBytes();
return new String(encodedKey);
}
public boolean checkCode(String secret, long code) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] decodedKey = BaseEncoding.base32().decode(secret);
// Window is used to check codes generated in the near past.
int window = WINDOW;
long currentInterval = getCurrentInterval();
for (int i = -window; i <= window; ++i) {
long hash = TOTP.generateTOTP(decodedKey, currentInterval + i, PASS_CODE_LENGTH, CRYPTO);
if (hash == code) {
return true;
}
}
return false;
}
}
Code
public class TwoFactorCodeManager {
@Autowired
private TwoFactorCodeRepo repo;
@Autowired
private TOTPUtils totpUtils;
public boolean hasLink(String userId) {
return repo.hasLink(userId);
}
public String generatorTotpCode(String userId) {
String totpCode = totpUtils.generateSecret();
return totpCode;
}
public boolean linkCode(String totpCode, long inputCode, String userId) throws InvalidKeyException, NoSuchAlgorithmException {
boolean verifySuccess = totpUtils.checkCode(totpCode, inputCode);
if (verifySuccess) {
repo.updateTotpCode(totpCode, userId);
}
return verifySuccess;
}
public boolean verifyCode(long inputCode, String userId) throws InvalidKeyException, NoSuchAlgorithmException {
String totpCode = repo.getTotpCode(userId);
return totpUtils.checkCode(totpCode, inputCode);
}
}
跟Spring Security整合的問題
- 如果要分兩頁登入,需改寫登入段相關程式
- 同時間如果有跟SSO整合,需改寫取得SSO Token的時機
參考文章
- http://wulfric.me/2014/09/design-of-sms-verification/
End
Two factor Authentication
By Mars Yang
Two factor Authentication
- 791