디지털 환경이 확장되면서 사용자는 수많은 서비스에 계정을 생성하고, 각각의 비밀번호를 관리해야 하는 부담을 느끼게 되었습니다. 이러한 상황에서 비밀번호가 유출될 경우 개인정보 유출이나 계정 탈취로 이어질 수 있으므로, 비밀번호 보안은 필수적인 요소입니다. 최근 일부 기업의 데이터베이스 유출 사고를 통해 알 수 있듯, 단순히 비밀번호를 저장하는 것이 아니라, 적절한 암호화 방식을 적용하는 것이 중요합니다.
본 글에서는 간단한 암호화 로직을 기반으로 한 비밀번호 변환 알고리즘을 소개합니다. 이 알고리즘은 복잡한 암호화 프로토콜 대신, 기본적인 문자 변환 규칙을 활용하여 비밀번호를 재구성하며, 특수문자 포함 여부를 검사하여 유효성을 판단합니다. 실제 시스템에서는 이와 같은 단순 알고리즘이 아닌, bcrypt, scrypt, 또는 Argon2와 같은 고도로 안전한 해시 함수를 권장하지만, 개념적 이해를 돕기 위한 예제로 제시됩니다.
public class PasswordEncoder {
private String encoded = "";
private char[] inputChars;
private char[] resultChars = new char[32];
// 소문자, 대문자, 숫자 배열 정의
private static final char[] LOWERCASE = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
private static final char[] UPPERCASE = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
private static final char[] DIGITS = {'0','1','2','3','4','5','6','7','8','9'};
public String encode(String password) {
int len = password.length();
if (len == 0) return "";
inputChars = new char[len];
for (int i = 0; i < len; i++) {
inputChars[i] = password.charAt(i);
// 특수문자 검사: 허용되지 않은 문자는 암호화 중단
if (isSpecialChar(inputChars[i])) {
return ""; // 오류 반환
}
}
// 문자별 변환 로직
for (int i = 0; i < len; i++) {
char c = inputChars[i];
if (contains(c, LOWERCASE)) {
int idx = findIndex(c, LOWERCASE);
int shifted = (idx + 5) % 26;
encoded += UPPERCASE[shifted];
} else if (contains(c, UPPERCASE)) {
int idx = findIndex(c, UPPERCASE);
int shifted = (idx + 5) % 26;
encoded += LOWERCASE[shifted];
} else if (contains(c, DIGITS)) {
int idx = findIndex(c, DIGITS);
int shifted = (idx + 3) % 10;
encoded += DIGITS[shifted];
} else if (c == '@') {
encoded += "_!#!";
}
}
// 결과 문자열 역순 처리
StringBuilder reversed = new StringBuilder(encoded);
encoded = reversed.reverse().toString();
return encoded;
}
// 특수문자 체크
private boolean isSpecialChar(char ch) {
return switch (ch) {
case '~', '`', '!', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', '|', '\\', '、', '{', '}', '【', '】', '[', ']', ';', ';', ':', ':', '‘', '’', '\'', '"', '"', '<', '>', '?', ',', ',', '。', '.', '/', ' ' -> true;
default -> false;
};
}
// 배열 내 특정 값 존재 여부 확인
private boolean contains(char ch, char[] arr) {
for (char c : arr) if (c == ch) return true;
return false;
}
// 인덱스 찾기
private int findIndex(char ch, char[] arr) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == ch) return i;
}
return -1;
}
}
이 코드는 웹 서버에서 동작하도록 설계된 Servlet과 함께 사용할 수 있습니다. 요청 파라미터로 전달된 비밀번호를 받아 암호화하고, 결과를 JSP 페이지로 전달하여 출력합니다.
@WebServlet("/encode")
public class EncodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
String rawPassword = request.getParameter("password");
PasswordEncoder encoder = new PasswordEncoder();
String encrypted = encoder.encode(rawPassword);
if (encrypted.isEmpty()) {
request.setAttribute("error", "비밀번호에 허용되지 않은 특수문자가 포함되어 있습니다.");
request.getRequestDispatcher("index.jsp").forward(request, response);
} else {
request.setAttribute("original", rawPassword);
request.setAttribute("result", encrypted);
request.getRequestDispatcher("result.jsp").forward(request, response);
}
}
}
또한, 사용자 입력 폼과 결과 출력 페이지는 다음과 같이 구성할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>비밀번호 암호화 도구</title>
</head>
<body>
<form action="encode" method="post">
<label>비밀번호:</label>
<input type="password" name="password" value="${param.password}">
<% if (request.getAttribute("error") != null) { %>
<span style="color: red;">${request.getAttribute("error")}</span>
<% } %>
<button type="submit">암호화</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>암호화 결과</title>
</head>
<body>
<h2>원본 비밀번호:</h2>
<p>${original}</p>
<h2>암호화된 결과:</h2>
<p>${result}</p>
</body>
</html>
이러한 구현은 교육 목적이나 단순한 테스트 용도로 활용될 수 있으며, 실제 생산 환경에서는 단방향 해시 함수와 솔트 추가가 반드시 필요합니다. 특히, 비밀번호는 절대 복호화 가능한 방식으로 저장해서는 안 됩니다.