사용하는 목적
WEB 서버에 SSL 설치 없이 로그인처리할때 평문으로 전송할경우 중간에서 정보를 가로채어 가로챈 계정정보를 권한이 없는 사용자가 시스템에 로그인 한후 시스템을 손상시킬수도 있습니다.
이와 같은 보안 문제가 발생하는 것을 방지하기 위해서 RSA암호화 방식을 사용합니다.
사실 그냥 SSL을 사용하면 더안전하게 아무런 처리없이 간단하게 사용할수 있지만 사용하는 서비스들에 대해서 전부 인증서를 등록(인증서별로 1년단위로 고정적으로 돈이 많이든다. ㅎㅎ)하여 사용 할수 없으니 가장 쉽고 돈안들이면서 안전하게 로그인 할수있는 방식이다.
RSA 란 ?
RSA란 암호화와 인증을 할수있는 공개키 암호시스템이다. 이것은 1977년 RonRivest와 Adi Shamir, Leonard Adleman에 의해서 개발되었다.
RSA 암호화 알고리즘을 통해서 평문을 암호화하여 서버로 전송하여 중간에 계정정보를 가로채더라도 암호화 키가 없으면 해석이 불가능하다.
기본 작동 원리
1. 서버측에서 RSA 공개키와 개인키(암호키)를 생성하여 개인키는 세션에 저장하고 공개키는 HTML 로그인 폼 페이지에 Input[type=hidden] value 값에 셋팅한다.
2. 로그인 폼은 사용자가 아이디 패스워드를 넣고 전송을 하면 전송하기전에 중간에 자바스크립트가 가로챈다.
3. 입력된 사용자 아이디,패스워드를 서버에서 전달받은 공개키로 RSA 암호화하여 서버로 전송한다.
4. 로그인폼에서 전달받은 사용자 아이디,패스워드를 세션에 저장된 RSA 개인키로 복호화 한다.
5. Database에 저장된 사용자 아이디와 패스워드가 일치하는지 확인한다.
Spring Framework RSA 키생성
로그인 폼 화면을 출력할때 공개키와 개인키를 생성하여 공개키는 Input hidden에 값을 셋팅하고 개인키는 세션에 저장한다.
1.JAVA에서 RSA를 사용하기위해서는 라이브러리를 Import 시킨다. ( 서버 )
1 2 3 4 5 6 7 8 | import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; |
2. 로그인 처리전이면 로그인폼에 공개키값을 전달한다. ( 서버 )
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 | //메인페이지 @RequestMapping(value = "/", method = RequestMethod.GET) public void mainLogout(HttpServletRequest request,HttpServletResponse response ) throws IOException { HttpSession session = request.getSession(); if( 로그인처리 완료 조건 ) { return "main"; //메인페이지 이동 } else //로그인처리 전이면 로그인 페이지로 이동한다. { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(1024); KeyPair keyPair = generator.genKeyPair(); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); session.setAttribute("_RSA_WEB_Key_", privateKey); //세션에 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); //로그인 폼에 Input Hidden에 값을 셋팅하기위해서 request.setAttribute("RSAExponent", publicKeyExponent); //로그인 폼에 Input Hidden에 값을 셋팅하기위해서 rest = "login"; } } |
3. 로그인 페이지에서 서버에서 전달된 공개키를 셋팅한다. ( 클라이언트 )
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 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>::RSA 테스트::</title> <script type="text/javascript" src="./lib/js/jquery-1.7.2.min.js"></script> <!-- RSA 자바스크립트 라이브러리 --> <script type="text/javascript" src="./lib/js/RSA/jsbn.js"></script> <script type="text/javascript" src="./lib/js/RSA/rsa.js"></script> <script type="text/javascript" src="./lib/js/RSA/prng4.js"></script> <script type="text/javascript" src="./lib/js/RSA/rng.js"></script> <!-- RSA 암호화 처리 스크립트 --> <script type="text/javascript" src="./lib/js/pages/main/login.js"></script> </head> <body> <div class="logBox"> <div class="logTitle"><img src="images/loginTitle_TMS.gif" alt="koscom TMS 로그인" title="koscom TMS 로그인" /></div> <div class="logField"> <form id="loginFrm"> <input type="hidden" id="RSAModulus" value="${RSAModulus}" /><!-- 서버에서 전달한값을 셋팅한다. --> <input type="hidden" id="RSAExponent" value="${RSAExponent}" /><!-- 서버에서 전달한값을 셋팅한다. --> <div class="inputField"> <p><input type="text" id="user_id" name="user_id" style="width:130px;" class="empty" emptyMsg="관리자 아이디를 입력하세요!" value=""/></p> <p><input type="password" id="user_pwd" name="user_pwd" style="width:130px;" class="empty" emptyMsg="패스워드를 입력하세요!" value=""/></p> </div> <div class="btn_login"> <input type="image" id="ms_login" style="cursor:pointer;cursor:hand;" title="로그인" alt="로그인버튼" src="images/btn_login.gif"> </div> </form> </div> </div> </body> </html> |
4. 사 용자 계정정보를 등록후 로그인 버튼을 클릭하면 서버로 전송되기전에 사용자 계정정보를 암호화후에 서버로 전달한다. ( 클라이언트)
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 | $("#ms_login").click(function(){ //사용자 계정정보 암호화전 평문 var uid = $("#user_id").val(); var pwd = $("#user_pwd").val(); //RSA 암호화 생성 var rsa = new RSAKey(); rsa.setPublic($("#OASISKeyModulus").val(), $("#OASISKeyExponent").val()); //사용자 계정정보를 암호화 처리 uid = rsa.encrypt(uid); pwd = rsa.encrypt(pwd); $.ajax({ type: "POST", url: "./proc/login.proc", data: {user_id:uid, user_pwd: pwd}, //사용자 암호화된 계정정보를 서버로 전송 dataType:"json", success: function(msg){ if(msg.state == "true") { location.href = "./"; } else if(msg.state == "false") { THIS.oWin.alert("로그인","로그인에 실패하였습니다. <br> 아이디 패스워드를 확인하여주세요."); } else { THIS.oWin.alert("로그인","잘못된 경로로 접근하였습니다. <br>암호화 인증에 실패하였습니다."); } } }); }); |
5. 로그인 폼에서 암호화처리된 사용자정보를 전송받아 복호화 처리후 . Database에 저장된 사용자 아이디와 패스워드가 일치하는지 확인한다.
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 | public String decryptRsa(PrivateKey privateKey, String securedValue) { String decryptedValue = ""; try{ Cipher cipher = Cipher.getInstance("RSA"); /** * 암호화 된 값은 byte 배열이다. * 이를 문자열 폼으로 전송하기 위해 16진 문자열(hex)로 변경한다. * 서버측에서도 값을 받을 때 hex 문자열을 받아서 이를 다시 byte 배열로 바꾼 뒤에 복호화 과정을 수행한다. */ byte[] encryptedBytes = hexToByteArray(securedValue); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); decryptedValue = new String(decryptedBytes, "utf-8"); // 문자 인코딩 주의. }catch(Exception e) { logger.info("decryptRsa Exception Error : "+e.getMessage()); } return decryptedValue; } /** * 16진 문자열을 byte 배열로 변환한다. */ 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; } /* 로그인 체크 */ @RequestMapping(value = "/proc/login.proc",headers="Accept=application/json",method = RequestMethod.POST) public @ResponseBody JSONObject loginChk(HttpServletRequest request, Map mMap ) { List<UserList> loginMember = null; JSONObject listObj = new JSONObject(); String uid = request.getParameter("user_id"); String pwd = request.getParameter("user_pwd"); HttpSession session = request.getSession(); PrivateKey privateKey = (PrivateKey) session.getAttribute("_RSA_WEB_Key_"); //로그인전에 세션에 저장된 개인키를 가져온다. if (privateKey == null) { listObj.put("state", "false"); } else { try { //암호화처리된 사용자계정정보를 복호화 처리한다. String _uid = decryptRsa(privateKey, uid); String _pwd = decryptRsa(privateKey, pwd); //복호화 처리된 계정정보를 map에 담아서 iBatis와 연동한다. mMap.put("user_id",_uid); mMap.put("user_pwd", _pwd); ....... iBatis 처리 및 로그인후 session 처리 } catch(Exception e) { listObj.put("state", "false"); logger.info("login ERROR : "+e.getMessage()); } } return listObj; } |
RSA 암호화 처리 끝..
머리속에있는걸 시간날때 정리할려고 막 끄집어 내고있는데 도통 머리속에서 나오지가 않는다.
뒤엉켜 있는 내머리속 ㅋㅋㅋㅋ
'Web Development > Spring Framework ' 카테고리의 다른 글
Spring quartz(스케줄러) 설정 (0) | 2014.06.01 |
---|---|
Spring MVC 프로젝트에서 소켓서버 구현방법 (0) | 2013.01.26 |
Spring + tiles 연동 (0) | 2013.01.14 |
Spring + iBatis + Eclipse 환경구축 및 iBatis 연동 설명 (0) | 2012.12.22 |