SILENTSOFT
Published on

Java로 OTP 구현하기

Authors

단 50줄이면 Java에서 OTP를 구현할 수 있다.

OTP를 구현해야겠다고 생각한 계기는, '클라이언트와 서버 간 REST 통신시, 해커로부터 데이터 변조 공격을 막기 위해서 일회용 비밀번호(OTP)를 사용하면 어떨까?' 라는 생각에서 출발했다. (적용은 안했지만, 누군가에게 도움이 되길 바라며...)

OTP (One Time Password)

고정된 패스워드 대신 무작위로 생성되는 일회용 패스워드를 이용하는 사용자 인증 방식.

은행에서 OTP 단말기나, OTP 카드를 사용해본 사람이라면 한 번쯤 생각하게 된다.

'서버랑 통신하는건가..?'

사실, OTP는 단말기와 서버가 통신하는게 아니다.

미리 정의된 비밀키와 시간 정보를 토대로 Hash 처리하여 비밀번호를 만들어낸다.

핵심은 시간이다. 시간은 변하므로, 일정 시간(정하기 나름) 마다 비밀번호가 바뀐다.

그리고, 똑같은 알고리즘으로 단말기에서 비밀번호를 생성하고, 서버에서 검증한다.

같은 시간대에 만들어졌으므로 단말기와 서버에서 만든 패스워드는 동일하다.

그럼 이제 OTP를 만들고 검증하는 클래스를 생성하고, 아래와 같이 상수를 정의하자.

private static final long DISTANCE = 30000; // 30 sec
private static final String ALGORITHM = "HmacSHA1";
private static final byte[] SECRET_KEY = "define your secret key here".getBytes();

그리고 비밀키와 시간에 따라 Hash 처리하여 패스워드를 생성하는 메소드를 아래와 같이 정의하자.

private static long create(long time) throws Exception {
	byte[] data = new byte[8];

	long value = time;
	for (int i = 8; i-- > 0; value >>>= 8) {
		data[i] = (byte) value;
	}

	Mac mac = Mac.getInstance(ALGORITHM);
	mac.init(new SecretKeySpec(SECRET_KEY, ALGORITHM));

	byte[] hash = mac.doFinal(data);
	int offset = hash[20 - 1] & 0xF;

	long truncatedHash = 0;
	for (int i = 0; i < 4; ++i) {
		truncatedHash <<= 8;
		truncatedHash |= hash[offset + i] & 0xFF;
	}

	truncatedHash &= 0x7FFFFFFF;
	truncatedHash %= 1000000;

	return truncatedHash;
}

마지막으로, OTP 클래스를 외부에서 사용할 수 있도록 public으로 create(), vertify() 메소드를 아래와 같이 정의하자.

public static String create() throws Exception {
	return String.format("%06d", create(new Date().getTime() / DISTANCE));
}

public static boolean vertify(String code) throws Exception {
	return create().equals(code);
}

테스트 코드는 아래와 같다.

String code = OTP.create();

System.out.println("Created OTP : " + code);

System.out.println("Vertify OTP : " + OTP.vertify(code));

30초(DISTANCE)마다 비밀번호가 변경되는 것을 확인할 수 있다.

참조 글 :