본문 바로가기

해킹-보안

Spring RSA 암호화 적용하기

반응형


구현 순서


- [서버] RSA 공개키를 자바스크립트 로그인 폼에 출력

- [클라] 로그인 시 자바스크립트에서 폼 데이터를 인터셉트 후 암호화 전송

- [서버] RSA 개인키로 수신한 폼 데이터를 복호화



ி 로그인 페이지 접근 - 서버 공개키 / 개인키 생성

 
@RequestMapping(value = "/", method = RequestMethod.GET)
	public String Login(HttpSession session, HttpServletRequest request,HttpServletResponse response, Model model) 
	 throws Exception, NoSuchAlgorithmException
	{
		  KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
		  generator.initialize(2048); // 키 사이즈 - 1024, 2048
		      
		  KeyPair keyPair = generator.genKeyPair();
		  KeyFactory keyFactory = KeyFactory.getInstance("RSA");
          
		  PublicKey publicKey = keyPair.getPublic();
		  PrivateKey privateKey = keyPair.getPrivate();
          
          // 개인 키 생성 후 세션에 저장
		  session.setAttribute("__rsaPrivateKey__", privateKey);
          
          // 공개키를 문자열로 변환
		  RSAPublicKeySpec publicSpec = keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
 
		  String publicKeyModulus = publicSpec.getModulus().toString(16);
		  String publicKeyExponent = publicSpec.getPublicExponent().toString(16);

		 // 로그인 폼 Input hidden 값 설정 
		  request.setAttribute("publicKeyModulus", publicKeyModulus);  
		  request.setAttribute("publicKeyExponent", publicKeyExponent); 
		
		return "Login";
}






ி 로그인 페이지 - 전송 데이터 암호화 

RSA 암호화를 위한 js 라이브러리

 
<title>Login</title>
	
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
	
    <!-- RSA 자바스크립트 라이브러리(순서 중요) -->
	<script type="text/javascript" src="/resources/js/RSA/jsbn.js"></script>
	<script type="text/javascript" src="/resources/js/RSA/rsa.js"></script>
	<script type="text/javascript" src="/resources/js/RSA/prng4.js"></script>
	<script type="text/javascript" src="/resources/js/RSA/rng.js"></script>
	
	
$("#loginBtn").on("click", function(){
			
				// 이용자 입력 값 저장 
				var uid = $("#userId").val();
				var pwd = $("#userPass").val();
			 
				// 서버로부터 수신한 공개키로 RSA 암호화 생성
				var rsa = new RSAKey();
				rsa.setPublic($("#rsaPublicKeyModulus").val(),$("#rsaPublicKeyExponent").val());
				
				// 이용자 입력 값 암호화 처리
				uid = rsa.encrypt(uid);
				pwd = rsa.encrypt(pwd); 
				
				// 암호화 값이 보이지 않게 hidden 로그인 폼으로 데이터 전달 
				$("#securedUsername").val(uid);
				$("#securedPassword").val(pwd);
  			    $("#securedLoginForm").submit();
				// location.href="/member/login.do";
			})
  
        <!-- 서버에서 전달받은 공개키를 hidden에 설정한다.  -->
        <input type="hidden" id="rsaPublicKeyModulus" value="${publicKeyModulus}" /> 
        <input type="hidden" id="rsaPublicKeyExponent" value="${publicKeyExponent}" />

		<h2>로그인</h2>
				<input type="text" id="userId" name="userId" style="margin-top:5px;">
			</div>
			<div>
				<input type="password" id="userPass" name="userPass">
			</div><br>
			<div>
				<button id="loginBtn" class="btn btn-success">로그인</button>
			</div>

<form id="securedLoginForm" name="securedLoginForm" action="/member/login.do" method="post" style="display: none;">
            <input type="hidden" name="securedUsername" id="securedUsername" value="" />
            <input type="hidden" name="securedPassword" id="securedPassword" value="" />

</form> 



ி 서버 개인키 - 데이터 복호화 

 
public class MemberController {

	// 복호화 함수 정의 
	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진수 문자열을 바이트 배열로 변환   
    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 = "/member/login.do", method = RequestMethod.POST) 
	  public String login(MemberVO vo,HttpServletRequest request,HttpServletResponse response, HttpSession session) throws Exception{ 

		  	// 세션에 저장된 개인키를 불러온다. 
		    PrivateKey privateKey = (PrivateKey)session.getAttribute("__rsaPrivateKey__");
	        session.removeAttribute("__rsaPrivateKey__"); // 키 재사용 방지
		
	        if (privateKey == null) {
	            throw new RuntimeException("암호화 비밀키 정보를 찾을 수 없습니다.");
	        }
	        
	        try {
                // 개인키로 데이터를 복호화한다.
	            String username = decryptRsa(privateKey, vo.getUserId());
	            String password = decryptRsa(privateKey, vo.getUserPass());
	            request.setAttribute("username", username);
	            request.setAttribute("password", password);
   
	        } catch (Exception ex) {
	            throw new ServletException(ex.getMessage(), ex);
	           
	        }
	        
	        return "redirect:/"; // 
	  }
}








반응형