비밀번호 전송 양방향 암호화(rsa) 적용
- view에서 서버로 계속 같은 비밀번호 전송하는걸 막기위해 rsa 양방향 암호화 적용
1.Rsa util 추가
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.HttpSession;
public class Rsa {
public static String RSA_WEB_KEY = "_RSA_WEB_Key_"; // 개인키 session key
public static String RSA_INSTANCE = "RSA"; // rsa transformation
/**
* 복호화
*
* @param privateKey
* @param securedValue
* @return
* @throws Exception
*/
public String decryptRsa(PrivateKey privateKey, String securedValue) throws Exception {
Cipher cipher = Cipher.getInstance(Rsa.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(Rsa.RSA_INSTANCE);
generator.initialize(1024);
KeyPair keyPair = generator.genKeyPair();
KeyFactory keyFactory = KeyFactory.getInstance(Rsa.RSA_INSTANCE);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
session.setAttribute(Rsa.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();
}
}
}
2. jsp에 필요 js 추가 및 hidden 값추가
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/rsa.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/jsbn.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/prng4.js"></script>
<script type="text/javascript" src="${_.contextPath}/resources/lib/RSA/rng.js"></script>
<form> ...
<input type="hidden" id="RSAModulus" value="${RSAModulus}"/>
<input type="hidden" id="RSAExponent" value="${RSAExponent}"/>
</form>
3. js에 암호화 로직 추가 및 암호화
// 비밀번호 암호화
function rsa(pwd){
var pw = pwd;
// rsa 암호화
var rsa = new RSAKey();
rsa.setPublic($('#RSAModulus').val(),$('#RSAExponent').val());
return rsa.encrypt(pw);
}
function bindLoginSubmit() {
$("#form_login").submit(function (e) {
e.preventDefault();
var form = this;
var options = {
method: form.method
/*, data: $(form).serialize()*/
, data: {
"username" : rsa($("input[name='username']").val())
,"password" : rsa($("input[name='password']").val())
}
};
})
};
4. WebSecurityConfig에 RequestContextListener 추가 (HttpRequest 불러오기위해)
@Bean
public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
5. 복호화 및 암호화 AuthenticationProvider에 추가
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
ServletRequestAttributes servletRequestAttribute = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = servletRequestAttribute.getRequest();
Rsa r = new Rsa();
HttpSession session = request.getSession();
//로그인전에 세션에 저장된 개인키를 가져온다.
PrivateKey privateKey = (PrivateKey) session.getAttribute(Rsa.RSA_WEB_KEY);
//암호화 된 비밀번호를 복호화 시킨다.
String username = r.decryptRsa(privateKey, (String) authentication.getPrincipal());
String password = r.decryptRsa(privateKey, (String) authentication.getCredentials());
// ShA 256 암호화 = 단방향
password = passwordEncoder.encode(password).replaceAll("\\{sha256}","");
}
}