2017. 4. 7. 14:00ㆍlanguage/jsp
javascript - java 간 RSA 를 이용해서 암호화 복호화 하기
JSP 기반으로 설명함. JSP가 아니라도 적용가능함.
|
프로젝트를 진행중에 로그인 시 로그인 정보의 평문전송에 대해서 이슈가 있었다.
RSA 를 사용하여 평문전송 이슈를 해결하였다.
RSA는 공개키 암호시스템의 하나로, 암호화뿐만 아니라 전자서명이 가능한 최초의 알고리즘으로 알려져 있다.
RSA에 관한 이론적인 설명은 없고 사용하는 방법과 소스를 첨부한다.
본 방법만을 가지고 보안처리를 하게 되면 CSRF attack, Session Hijacking, XSS 취약점에 노출 될 수 있습니다.
CSRF attack, Session Hijacking, XSS 의 취약점에 대해서도 대응해야 합니다.
요약하자면 다음과 같다.
controller 에서 공개키와 개인키를 생성한다.
개인키는 session 에 저장한다.
공개키를 사용하여 modulus, exponent 를 생성하고 request 에 담는다.
loginForm.jsp를 호출한다.
입력받은 id, pw 등을 modulus, exponent 사용하여 rsa 암호화 한 후 전송한다.
javascript 에서 rsa를 사용하기 위한 js 파일들 -> (biginteger javascript 링크 또는 biginteger javascript.zip)
전송받은 controller 에서 session 에 저장했던 개인키를 활용하여 복호화 한다.
소스 설명은 주석으로 대신한다.
Login Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 |
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.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller("loginController")
public class LoginController {
private static String RSA_WEB_KEY = "_RSA_WEB_Key_"; // 개인키 session key
private static String RSA_INSTANCE = "RSA"; // rsa transformation
// 로그인 폼 호출
@RequestMapping("/loginForm.do")
public ModelAndView loginForm(HttpServletRequest request, HttpServletResponse response) throws Exception {
// RSA 키 생성
initRsa(request);
ModelAndView mav = new ModelAndView();
mav.setViewName("loginForm");
return mav;
}
// 로그인
@RequestMapping("/login.do")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws Exception {
String userId = (String) request.getParameter("USER_ID");
String userPw = (String) request.getParameter("USER_PW");
HttpSession session = request.getSession();
PrivateKey privateKey = (PrivateKey) session.getAttribute(LoginController.RSA_WEB_KEY);
// 복호화
userId = decryptRsa(privateKey, userId);
userPw = decryptRsa(privateKey, userPw);
// 개인키 삭제
session.removeAttribute(LoginController.RSA_WEB_KEY);
// 로그인 처리
/*
...
*/
ModelAndView mav = new ModelAndView();
mav.setViewName("index");
return mav;
}
/**
* 복호화
*
* @param privateKey
* @param securedValue
* @return
* @throws Exception
*/
private String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception {
Cipher cipher = Cipher.getInstance(LoginController.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(LoginController.RSA_INSTANCE);
generator.initialize(1024);
KeyPair keyPair = generator.genKeyPair();
KeyFactory keyFactory = KeyFactory.getInstance(LoginController.RSA_INSTANCE);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
session.setAttribute(LoginController.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();
}
}
} |
cs |
JSP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 |
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String ctxPath = (String) request.getContextPath();
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login</title>
<script type="text/javascript" src="<%=ctxPath %>/script/jquery/jquery-1.11.0.min.js"></script>
<!-- 순서에 유의 -->
<script type="text/javascript" src="<%=ctxPath %>/script/RSA/rsa.js"></script>
<script type="text/javascript" src="<%=ctxPath %>/script/RSA/jsbn.js"></script>
<script type="text/javascript" src="<%=ctxPath %>/script/RSA/prng4.js"></script>
<script type="text/javascript" src="<%=ctxPath %>/script/RSA/rng.js"></script>
<script type="text/javascript">
function login(){
var id = $("#USER_ID_TEXT");
var pw = $("#USER_PW_TEXT");
if(id.val() == ""){
alert("아이디를 입력 해주세요.");
id.focus();
return false;
}
if(pw.val() == ""){
alert("비밀번호를 입력 해주세요.");
pw.focus();
return false;
}
// rsa 암호화
var rsa = new RSAKey();
rsa.setPublic($('#RSAModulus').val(),$('#RSAExponent').val());
$("#USER_ID").val(rsa.encrypt(id.val()));
$("#USER_PW").val(rsa.encrypt(pw.val()));
id.val("");
pw.val("");
return true;
}
</script>
</head>
<body>
<form name="frm" method="post" action="<%=ctxPath%>/login.do" onsubmit="return login()">
<input type="hidden" id="RSAModulus" value="${RSAModulus}"/>
<input type="hidden" id="RSAExponent" value="${RSAExponent}"/>
<input type="text" placeholder="아이디" id="USER_ID_TEXT" name="USER_ID_TEXT" />
<input type="password" placeholder="비밀번호" id="USER_PW_TEXT" name="USER_PW_TEXT" />
<input type="hidden" id="USER_ID" name="USER_ID">
<input type="hidden" id="USER_PW" name="USER_PW">
<input type="submit" value="로그인" />
</form>
</body>
</html> |
cs |
'language > jsp' 카테고리의 다른 글
jsp 홈페이지 주소 가져오기 / 서버주소 가져오기 (0) | 2017.02.06 |
---|---|
jstl 문자열 배열 처리하기 / split forEach / forTokens (0) | 2016.11.23 |
jstl fn 정리 / jstl functions 정리 (0) | 2016.11.23 |
jstl 날짜 차이 계산하기 / parseDate / parseNumber (0) | 2016.11.23 |
Jsp 커스텀 태그라이브러리(Custom Tag Library Descriptor) 생성 및 사용 (2) | 2016.11.11 |
[필독][기초] / JSP [part 4] (0) | 2016.01.05 |
[필독][기초] / JSP [part 3] (0) | 2016.01.05 |
[필독][기초] / JSP [part 2] (0) | 2016.01.05 |
[필독][기초] / JSP [part 1] (0) | 2016.01.05 |
[필독][기초] / JSP 목차 (0) | 2016.01.05 |