본문 바로가기
개발/세상에서 가장 간단한 로그인

2-2. 비밀번호 단방향 암호화하기(SHA-512)

by 하모예 2020. 10. 21.

(이 글은 기본적으로 Eclipse, Sql Developer가 설치, 인코딩 처리, 서버 등록이 완료된 상태를 가정하고 작성되었습니다.)

  • 사용 기술 : Java, JSP, Oracle, Bootstrap, CSS, HTML
  • 오늘의 기술 : JAVA
  • 사용 툴 : Eclipse

안녕하세요 하모예입니다. 

JAVA 두번째 포스팅에서 다룰 기술은 단방향 암호화입니다. 

암호화는 크게 양방향과 단방향으로 나눠지는데, 이름에서 알 수 있듯이

양방향은 암호화와 복호화가 가능하고, 단방향은 암호화만 가능하고 복호화는 불가능합니다. 

그중 우리가 지금 만들고 있는 '세상에서 가장 간단한 로그인'에는 둘 중에 단방향 암호화가 필요합니다. 

 

가령 아이디나 비밀번호를 잊어버렸을 경우 아이디는 아이디 찾기를 통해 

가입할 때 입력했던 정보들로 "내 아이디"를 찾을 수 있지만, 

비밀번호는 새로운 비밀번호를 입력하거나, 

불규칙한 숫자와 문자들의 조합으로 이루어진 임시 비밀번호를 입력했던 경험이 있으실 거예요. 

 

이때 아이디는 찾을 수 없지만 비밀번호를 새롭게 입력해야하는 이유가 암호화에 있습니다. 

개인정보 보호법에 따라서 비밀번호는 "일방향(해쉬함수) 암호화"가 필수적이기 때문이죠. 

개발자도, 아이디의 주인도, 그 누구도 알 수 없는 방법으로 암호화가 되어 있으니

기존의 비밀번호를 찾을 수가 없습니다.

궁금하신 분은 아래 사이트로 가시면 법령부터 자세한 설명까지 모두 보실 수 있습니다. 

www.privacy.go.kr/

 

개인정보보호 포털

공지 2020년 개인정보 영향평가 인증시험(2차, 10.31) 안내 ※ 코로나19 확산추이에 따라 '20년 마지막 인증시험이 될 수 있으니 평가대상자는 아래 내용을 확인하시어 꼭 응시하시기 바랍니다. ※ 

www.privacy.go.kr

저는 개발자가 뛰어난 기능을 개발하는 것도 중요하지만, 

지켜야 하는 법률을 어기지 않으면서 개발을 하는 것도 못지않게 중요하다고 생각합니다.

사담이 길었네요. 

그러면 단방향 암호화 로직을 구성해봅시다.

아주 단순하게 설명하면 단방향 암호화는 Wrapper를 통해서 JSP에서 Servlet으로 넘어가는 정보를

중간에 캐치해서 변형시킨다음 Listener를 통해서 변형된 정보를 원하는 url pattern에 적용해주는 과정입니다.

지난시간에 만들었던  login.common.filter 패키지에 EncryptorWrapper 클래스를 만들어주세요.

이때 Superclass옆에 Browse를 눌러서 HttpServletRequestWrapper를 상속받아줍니다.

EncryptorWrapper클래스가 생성되면 에러가 납니다.

HttpServletRequestWrapper를 상속했기 때문에 이 친구가 포함된 생성자를 필수적으로 만들어줘야 하기 때문에 생기는 에러이므로 빨간줄 위에 마우스를 올려서 Add constructor를 눌러서 생성자를 만들어주면 에러가 사라집니다.

단방향 암호화 함수중에서 라이브러리 없이도 사용할 수 있는 SHA-512를 사용해봅시다. 

먼저 지나가는 String값을 낚아채서 암호화할 수 있는 암호화 메소드를 만듭니다. 

	private String getSha512(String value) {
		//암호화 완료된 값 보관할 변수
		String encPwd=null;
		//알고리즘 불러오기 위한 객체
		MessageDigest md = null;
		try {
			md=MessageDigest.getInstance("SHA-512");
		}catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		//알고리즘을 이용해서 byte단위로 암호화 처리
		byte[] bytes=value.getBytes(Charset.forName("UTF-8"));
		//MessageDigest 객체에 있는 update 메소드를 활용해서 byte값을 단방향 암호화 처리 함
		md.update(bytes);
		//byte단위로 암호화한 내용을 String 값으로 변환해서 encPwd에 넣어줌.
		encPwd=Base64.getEncoder().encodeToString(md.digest());
		//encPwd 반환
		return encPwd;
	}

그 다음 이 메소드를 활용해서 HttpRequestWrapperServlet에 있는 getParameter를 오버라이드를 통해 변형해줍니다.

알고 있으시겠지만 메소드를 재정의하고 싶을때 Override를 씁니다.

	@Override
	public String getParameter(String name) {
		String after="";
		//클라이언트가 전달한 값 중에서 비밀번호만 암호화.
		if(name.equals("userPw")) {
			String password = super.getParameter(name);
			//getSha512메소드에 getParameter로 불러온 데이터 매개변수로 입력
			String encPw = getSha512(password);
			after = encPw;
		//비밀번호가 아닐 경우에는 암호화처리 없이 그대로 반환.
		}else {
			after = super.getParameter(name);
		}
		return after;
	}

조금 더 자세히 설명하자면 getParameter에서 매개변수로 불려온 name값이 userPw인 경우에 

request.getParameter("userPw")값을 getSha512에 매개변수로 넣어서 암호화한 다음 그 값을 반환해주고

그렇지 않은 경우에는 처음에 request.getParamter("")로 들어온 값 그대로 반환해주는 로직입니다.

Wrapper를 만든 다음 Listener를 통해서 Wrapper를 원하는 url pattern과 매핑해줍니다.

Filter를 만드는 법은 EncodingFilter를 만들었을때와 동일합니다. 

다만 한가지 차이가 있다면 EncryptorFilter를 WebFilter 어노테이션에 servletNames를 적어준다는 점입니다.

EncryptorFilter의 코드는 다음과 같습니다. 

package login.common.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

/**
 * Servlet Filter implementation class EncryptorFilter
 */
@WebFilter(servletNames = {"login"})
public class EncryptorFilter implements Filter {

    /**
     * Default constructor. 
     */
    public EncryptorFilter() {
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		// TODO Auto-generated method stub
	}

	/**
	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		// place your code here
		EncryptorWrapper ew = new EncryptorWrapper((HttpServletRequest)request);
		// pass the request along the filter chain
		chain.doFilter(ew, response);
	}

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig fConfig) throws ServletException {
		// TODO Auto-generated method stub
	}

}

 

우리가 단방향 암호화를 해주고 싶은 서블릿의 name을 servletnames에 적어줍니다.

그리고 doFilter메소드에 EncryptorWrapper를 불러온 다음 HttpServletRequest를 매개변수로 입력해주고

chain.doFilter(request,response)를 chain.doFilter(ew,response)로 변경해주면 끝!

인코딩, 단방향 암호화까지 끝났으니 다음 포스팅에서는 우리가 변형시킨 getParameter 메소드를 활용해서

클라이언트의 아이디와 비밀번호를 불러오겠습니다.

댓글