비밀번호 전송 양방향 암호화(rsa) 적용

  • view에서 서버로 계속 같은 비밀번호 전송하는걸 막기위해 rsa 양방향 암호화 적용

1.Rsa util 추가

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class Rsa {

    public static String RSA_WEB_KEY = "_RSA_WEB_Key_"; // 개인키 session key
    public static String RSA_INSTANCE = "RSA"; // rsa transformation

    /**
     * 복호화
     *
     * @param privateKey
     * @param securedValue
     * @return
     * @throws Exception
     */
    public String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception {
        Cipher cipher = Cipher.getInstance(Rsa.RSA_INSTANCE);
        byte[] encryptedBytes = hexToByteArray(securedValue);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        String decryptedValue = new String(decryptedBytes, "utf-8"); // 문자 인코딩 주의.
        return decryptedValue;
    }

    /**
     * 16진 문자열을 byte 배열로 변환한다.
     *
     * @param hex
     * @return
     */
    public static byte[] hexToByteArray(String hex) {
        if (hex == null || hex.length() % 2 != 0) { return new byte[] {}; }

        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            byte value = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
            bytes[(int) Math.floor(i / 2)] = value;
        }
        return bytes;
    }

    /**
     * rsa 공개키, 개인키 생성
     *
     * @param request
     */
    public void initRsa(HttpServletRequest request) {
        HttpSession session = request.getSession();

        KeyPairGenerator generator;
        try {
            generator = KeyPairGenerator.getInstance(Rsa.RSA_INSTANCE);
            generator.initialize(1024);

            KeyPair keyPair = generator.genKeyPair();
            KeyFactory keyFactory = KeyFactory.getInstance(Rsa.RSA_INSTANCE);
            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();

            session.setAttribute(Rsa.RSA_WEB_KEY, privateKey); // session에 RSA 개인키를 세션에 저장

            RSAPublicKeySpec publicSpec = (RSAPublicKeySpec) keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
            String publicKeyModulus = publicSpec.getModulus().toString(16);
            String publicKeyExponent = publicSpec.getPublicExponent().toString(16);

            request.setAttribute("RSAModulus", publicKeyModulus); // rsa modulus 를 request 에 추가
            request.setAttribute("RSAExponent", publicKeyExponent); // rsa exponent 를 request 에 추가
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

2. jsp에 필요 js 추가 및 hidden 값추가

<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/rsa.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/jsbn.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/prng4.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/rng.js"></script>
<form> ...
                                <input type="hidden" id="RSAModulus" value="${RSAModulus}"/>
                                <input type="hidden" id="RSAExponent" value="${RSAExponent}"/>
</form>

3. js에 암호화 로직 추가 및 암호화

// 비밀번호 암호화
function rsa(pwd){
    var pw = pwd;
    // rsa 암호화
    var rsa = new RSAKey();
    rsa.setPublic($('#RSAModulus').val(),$('#RSAExponent').val());
    return rsa.encrypt(pw);
}
function bindLoginSubmit() {
  $("#form_login").submit(function (e) {
    e.preventDefault();
    var form = this;
    var options = {
      method: form.method
      /*, data: $(form).serialize()*/
      , data: {
          "username" :  rsa($("input[name='username']").val())
          ,"password" : rsa($("input[name='password']").val())
      }
    };
})
};

4. WebSecurityConfig에 RequestContextListener 추가 (HttpRequest 불러오기위해)

    @Bean
    public RequestContextListener requestContextListener(){
        return new RequestContextListener();
    }

5. 복호화 및 암호화 AuthenticationProvider에 추가

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        try {
            ServletRequestAttributes servletRequestAttribute = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request = servletRequestAttribute.getRequest();
            Rsa r = new Rsa();
            HttpSession session = request.getSession();
            //로그인전에 세션에 저장된 개인키를 가져온다.
            PrivateKey privateKey = (PrivateKey) session.getAttribute(Rsa.RSA_WEB_KEY);
            //암호화 된 비밀번호를 복호화 시킨다.
            String username = r.decryptRsa(privateKey, (String) authentication.getPrincipal());
            String password = r.decryptRsa(privateKey, (String) authentication.getCredentials());
            // ShA 256 암호화 = 단방향
            password = passwordEncoder.encode(password).replaceAll("\\{sha256}","");
        }
}

+ Recent posts