Programming
No. | 738 |
Name. | swindler |
Subject. | javascript - RSA로 로그인 암호화 |
Main Cate. | Javascript |
Sub Cate. | HTML |
Date. | 2014-01-16 11:24 |
Hit. | 7400 (211.36.27.3) |
File. | |
로그인폼 등에서 값이 전송되는 경우 패킷스니핑을 통하여 값을 파악할 수 있는 문제가 발생한다. 가장 쉬운 방법은 SSL을 사용하여 전송계층을 암호화하는 것인데, SSL을 사용하지 않는 경우에는 비밀번호등이 노출되는 위험이 있다. 이 경우 javascript를 통하여 RSA로 암호화를 할 수 있다. 로그인폼의 데이터를 RSA 공개키로 암호화해서 서버로 전송하므로, 중간에 가로채도 값을 알수가 없다. 이 경우 BigIntegers and RSA in Javascript 라이브러리를 사용할 수 있다. 참고로 이 라이브러리의 BASE64 인코더에 문제가 있다는 글들이 있다. 파폭에서 오동작을 한다나. 1. 공개키와 개인키를 생성 KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(KEY_SIZE); KeyPair keyPair = generator.genKeyPair(); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); HttpSession session = request.getSession(); // 세션에 공개키의 문자열을 키로하여 개인키를 저장한다. session.setAttribute("__rsaPrivateKey__", privateKey); // 공개키를 문자열로 변환하여 JavaScript RSA 라이브러리 넘겨준다. RSAPublicKeySpec publicSpec = (RSAPublicKeySpec) keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class); String publicKeyModulus = publicSpec.getModulus().toString(16); String publicKeyExponent = publicSpec.getPublicExponent().toString(16); request.setAttribute("publicKeyModulus", publicKeyModulus); request.setAttribute("publicKeyExponent", publicKeyExponent); request.getRequestDispatcher("/WEB-INF/views/loginForm.jsp").forward(request, response); 2. HTML <!-- script 태그에서 가져오는 자바스크립트 파일의 순서에 주의해야한다! 순서가 틀릴경우 자바스크립트 오류가 발생한다. --> <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/jsbn.js"></script> <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/rsa.js"></script> <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/prng4.js"></script> <script type="text/javascript" src="<%=request.getContextPath()%>/js/rsa/rng.js"></script> <script type="text/javascript" src="<%=request.getContextPath()%>/js/login.js"></script> </head> <body> <div> <label for="username">사용자ID : <input type="text" id="username" size="16"/></label> <label for="password">비밀번호 : <input type="password" id="password" size="16" /></label> <input type="hidden" id="rsaPublicKeyModulus" value="<%=publicKeyModulus%>" /> <input type="hidden" id="rsaPublicKeyExponent" value="<%=publicKeyExponent%>" /> <a href="<%=request.getContextPath()%>/loginFailure.jsp" onclick="validateEncryptedForm(); return false;">로그인</a> </div> <form id="securedLoginForm" name="securedLoginForm" action="<%=request.getContextPath()%>/login" method="post" style="display: none;"> <input type="hidden" name="securedUsername" id="securedUsername" value="" /> <input type="hidden" name="securedPassword" id="securedPassword" value="" /> </form> </body> 엔터를 쳐서 submit이 되는것을 막아야 한다. 위 예의 경우에는 입력용 폼과 submit용 폼을 분리한 것인데, 그닥 좋은 방법은 아닌것 같다. 3. javascript 암호화 function validateEncryptedForm() { var username = document.getElementById("username").value; var password = document.getElementById("password").value; if (!username || !password) { alert("ID/비밀번호를 입력해주세요."); return false; } try { var rsaPublicKeyModulus = document.getElementById("rsaPublicKeyModulus").value; var rsaPublicKeyExponent = document.getElementById("rsaPublicKeyExponent").value; submitEncryptedForm(username,password, rsaPublicKeyModulus, rsaPublicKeyExponent); } catch(err) { alert(err); } return false; } function submitEncryptedForm(username, password, rsaPublicKeyModulus, rsaPpublicKeyExponent) { var rsa = new RSAKey(); rsa.setPublic(rsaPublicKeyModulus, rsaPpublicKeyExponent); // 사용자ID와 비밀번호를 RSA로 암호화한다. var securedUsername = rsa.encrypt(username); var securedPassword = rsa.encrypt(password); // POST 로그인 폼에 값을 설정하고 발행(submit) 한다. var securedLoginForm = document.getElementById("securedLoginForm"); securedLoginForm.securedUsername.value = securedUsername; securedLoginForm.securedPassword.value = securedPassword; securedLoginForm.submit(); 4. 서버측 복호화 /** * 암호화된 비밀번호를 복호화 한다. */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String securedUsername = request.getParameter("securedUsername"); String securedPassword = request.getParameter("securedPassword"); HttpSession session = request.getSession(); PrivateKey privateKey = (PrivateKey) session.getAttribute("__rsaPrivateKey__"); session.removeAttribute("__rsaPrivateKey__"); // 키의 재사용을 막는다. 항상 새로운 키를 받도록 강제. if (privateKey == null) { throw new RuntimeException("암호화 비밀키 정보를 찾을 수 없습니다."); } try { String username = decryptRsa(privateKey, securedUsername); String password = decryptRsa(privateKey, securedPassword); request.setAttribute("username", username); request.setAttribute("password", password); request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response); } catch (Exception ex) { throw new ServletException(ex.getMessage(), ex); } } private String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception { System.out.println("will decrypt : " + securedValue); Cipher cipher = Cipher.getInstance("RSA"); 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 배열로 변환한다. */ 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; } 참고자료 http://wiki.kldp.org/wiki.php/DocbookSgml/SSL-Certificates-HOWTO SSL 인증서 HOWTO http://kwon37xi.egloos.com/4427199 RSA기반 웹페이지 암호화 로그인 : 내가 하고자 하던 내용에 대한 기본 작업, 마지막 항목에 나와 있는 이중 암호화를 추가함 http://ohdave.com/rsa/ 자바 스크립트로 구현한 RSA http://www.fourmilab.ch/javascrypt/ 자바 스크립트 기반 암호화 툴들 http://people.eku.edu/styere/ 자바스크립트 기반 암호화 예제 http://www-cs-students.stanford.edu/~tjw/jsbn/ RSA, BigInteger 소스 : 이곳 자바스크립트 소스를 RSA Encrypt용으로 사용함 http://www.movable-type.co.uk/ 자바스크립트 암호화 : TEA 라는 암호화 방식으로 이중암호화용으로 사용함 [바로가기 링크] : http://coolx.net/cboard/develop/738 |
|
|
|
[Modify] [Delete] | [Reply] [List] |