3일을 고생한 RSA !! -> 간단한 원리를 알면 고생할 필요가 없었다 ^^....
LoginController
@Controller
public class LoginController {
// 아이디 암호화
@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);
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);
System.out.println("publicKeyModulus: "+publicKeyModulus);
System.out.println("publicKeyExponent: "+publicKeyExponent);
return "login/login"; // jsp
}
// 복호화 함수 정의
private String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception {
logger.info("decryptRsa :");
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 = "/login", method = RequestMethod.POST)
public String login(HttpServletRequest request, HttpServletResponse response, HttpSession session)
throws Exception {
logger.info("login start:");
String securedUser_Id = (String) request.getParameter("securedUser_Id");
String securedUser_Pwd = (String) request.getParameter("securedUser_Pwd");
System.out.println("securedUser_Id: "+securedUser_Id.toString());
System.out.println("securedUser_Pwd: "+securedUser_Pwd.toString());
//세션에 저장된 개인키를 불러온다.
PrivateKey privateKey = (PrivateKey) session.getAttribute("__rsaPrivateKey__");
session.removeAttribute("__rsaPrivateKey__"); // 키 재사용 방지
if (privateKey == null) {
throw new RuntimeException("암호화 비밀키 정보를 찾을 수 없습니다.");
}
try {
// 개인키로 데이터를 복호화한다.
String id = decryptRsa(privateKey, securedUser_Id);
String pwd = decryptRsa(privateKey, securedUser_Pwd);
request.setAttribute("id", id);
request.setAttribute("pwd", pwd);
System.out.println("id: "+id);
System.out.println("pwd: "+pwd);
} catch (Exception ex) {
throw new ServletException(ex.getMessage(), ex);
}
return "redirect:/"; //
}
}
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8" />
<title>로그인</title>
<!-- jquery + bootstrap-->
<script type="text/javascript" src="plugins/jquery/jquery-3.3.1.min.js"></script>
<link rel="stylesheet" href="plugins/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="plugins/bootstrap/js/bootstrap.min.js">
<!-- css -->
<link rel="stylesheet" href="/css/login/login.css">
<!-- js -->
<script src="/js/login/login.js"></script>
<!-- 순서에 유의 -->
<script type="text/javascript" src="plugins/rsa/jsbn.js"></script>
<script type="text/javascript" src="plugins/rsa/rsa.js"></script>
<script type="text/javascript" src="plugins/rsa/prng4.js"></script>
<script type="text/javascript" src="plugins/rsa/rng.js"></script>
</head>
<body>
<div class="login_all">
<div class="login_menu">
<a href="/"><img alt="logo" src="/images/logo.png" height="50" width="100"></a>
<!-- 서버에서 전달받은 공개키를 hidden에 설정한다. -->
<input type="hidden" id="rsaPublicKeyModulus" value="${publicKeyModulus}" />
<input type="hidden" id="rsaPublicKeyExponent" value="${publicKeyExponent}" />
<div>
<label>ID</label>
<input type="text" id="id" name="id" autocomplete="off" class="id_inp">
</div>
<div>
<label>PW</label>
<input type="password" id="pwd" name="pwd" autocomplete="off" class="pw_inp">
</div>
<div>
<a class="btn btn-secondary" href="/login" onclick="validateRSA(); return false;">로그인</a>
<!-- <button class="btn btn-secondary" type="button" onclick="callFuntion(login)">로그인</button> -->
</div>
<form id="frm" name="frm" method="post" action="/login">
<input type="hidden" name="securedUser_Id" id="securedUser_Id"value="" />
<input type="hidden" name="securedUser_Pwd" id="securedUser_Pwd" value="" />
</form>
</div>
</div>
</body>
</html>
login.js
function validateRSA() {
var id = document.getElementById("id").value;
var pwd = document.getElementById("pwd").value;
try {
var rsa = new RSAKey();
rsa.setPublic($('#rsaPublicKeyModulus').val(), $('#rsaPublicKeyExponent').val());
// 사용자ID, 비밀번호를 RSA로 암호화
var securedUser_Id = rsa.encrypt(id);
var securedUser_Pwd = rsa.encrypt(pwd);
var frm = document.getElementById("frm");
frm.securedUser_Id.value = securedUser_Id;
frm.securedUser_Pwd.value = securedUser_Pwd;
frm.submit();
} catch(e) {
alert(e);
}
}
login.css
.login_all {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.login_menu {
text-align: center;
margin-bottom: 12%;
}
.id_inp {
margin-left: 18px;
width: 86%;
text-align: center;
}
.pw_inp {
margin-left: 9px;
width: 86%;
text-align: center;
}
.btn-secondary {
width: 100%;
margin-top: 3px;
margin-bottom: 1px;
}
.user_bt {
margin-right: 5px;
}
.user_bt>button {
background: none;
border: none;
}
.user_bt>button:hover {
color: #8d8d8d;
}
.user_bt>button:active {
color: #4e4e4e;
}
출처:
https://mylife365.tistory.com/501
https://jdh5202.tistory.com/766#google_vignette