JSP(사용자 정보 암호화 / 복호화 처리)
사용자 정보 암호화 / 복호화 처리
암호화 종류
1. 양방향 암호화
1.1 대칭키 암호화 : 송,수신 평문을 암호화하고 복호화를 진행하면서 암호화시 활용된 보안키값을 복호화하여 평문으로
변경시에도 활용하는것을 의미함.
1.1.1 특징 : 공개키 암호화에비해 암호화/복호화를 통한 송,수신 속도가 빠름.
암호화되어 송신되는 전송 데이타의 길이가 공개키에비해 짧음.
1.1.2 암호화 알고리즘: SEED(KISA 권장), ARIA, DES, MISTY
1.2 공개키 암호화 : 수신 측에서는 암호화 송신을위해 비밀키와 공개키를 생성하고 송신측에게
공개키(public)를 제공해 (비대칭키 암호화)송신측에서는 제공된 공개키를 이용해
평문을 암호화해 전송하고, 수신측에서는 비밀키(private)를 활용해 수신 데이타를
평문으로 변경함.
1.2.1 특징 : 암호화 속도가 대칭키 암호화에비해 느림.
1.2.2 암호화 알고리즘 : RSA(KISA 권장), KCDSA, ECC
1.3 해쉬 암호화 : 해쉬함수(송신 정보를 작은 데이타[해쉬코드]로 재가공하며, 난수를 활용함.)
를 이용해 암호화한 평문을 송신.
해쉬함수를 이용해 암호화된 데이타는 복호화될수 없음.
수신측에서는 실 수신문 대상의 검증을위한 데이타 지문으로 활용됨.
1.3.1 암호화 알고리즘 : SHA1|2|SHA256(KISA 권장)|SHA512, MD5
2. 단방향 암호화
2. 암호화 알고리즘
MD5(Message Digest Algorithm 5) : 128bit hashcode를 제공하는 단방향(암호화) 알고리즘 기법으로
2006년도 복호화되는 결함이 발견되어 더이상 암호화 알고리즘으로
사용되지 않음.(위변조가 가능함)
Base64Util : 2진 데이터를 64개의 아스키 텍스트로 변환하거나 연변환하는 인코딩 기법으로
MD5를 통해 암호화 해싱된값을 문자열로 출력시 활용되며, 원본 해싱값보다 33% 값이 커짐.
송신 대상의 평문을 아스키 코드로 변환하고 2진화데이타로 변환시키고,
변환된 2진데이타를 4개의 6bit씩으로 다시 변환처리.
SHA[1|2] (Secure Hash Algorithm) : MD5의 위변조 결함에 대안으로 마련된 단방향 암호화 알고리즘.
한국정보보호협회(KISA의 권유로 SHA1+salt를 권장하고 있음.).
Rainbow Table을 통해 복호화될 가능성이 존재하는 불완전한 알고리즘.
SHA1|SHA2 사용불가. SHA256|SHA512 활용 추천.
AES(Advanced Encryption Standard) : 미 연방 표준 차세대 128bit 암호화 알고리즘으로 암/복호화 가능.
AES(Advanced Encryption Standard) : 미 연방 표준 차세대 128bit 암호화 알고리즘으로 암/복호화 가능.
RSA(MIT의 Ronald Rivest, Adi Shamir, Leconard Adleman 3인에의해 개발된 공개키 암호화 알고리즘)
스탠포드대에서 배포한 javascript 기반 RSA and ECC in JavaScript 라이브러리를 이용한 암호화시 속도비교
www-cs-students.stanford.edu/~tjw/jsbn/)
Chrome FireFox IE Mobile
RSA public, 512 bit, e=3 0ms 1ms 4ms 40ms
RSA public, 512 bit, e=F4 1ms 6ms 20ms 140ms
RSA public, 1024 bit, e=3 1ms 3ms 10ms 90ms
RSA public, 1024 bit, e=F4 2ms 15ms 70ms 180ms
공개키 비밀키 만드는 예제
[CryptoGenerator.java]
package kr.or.ddit.utiles;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
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;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.http.HttpSession;
public class CryptoGenerator {
// 암, 복호화 : 공개키 + 비밀키 (생성시 동반 생성, 1회 활용하고 폐기)
// 반환값 : 공개키(가수부, 지수부 구분)
public static Map<String, String> generatePairKey(HttpSession session) {
// 공개키 + 비밀키 생성
KeyPairGenerator keyGenerator = null;
// 생성된 공개키 + 비밀키
KeyPair keyPair = null;
//공개키
PublicKey publicKey = null;
//비밀키
PrivateKey privateKey = null;
// 공개키 = 가수부 + 지수부
KeyFactory keyFactory = null;
Map<String, String> publicKeyMap = new HashMap<String, String>();
try{
keyGenerator = KeyPairGenerator.getInstance("RSA");
// 공개키, 비밀키 생성시 사이즈 설정 : byte 단위
// 사이즈를 결정하는데 반드시 짝수이어야 함
keyGenerator.initialize(2048);
//생성된 공개키, 비밀키 취득
keyPair = keyGenerator.generateKeyPair();
//공개키 취득
publicKey = keyPair.getPublic();
//비밀키 취득
privateKey = keyPair.getPrivate();
session.setAttribute("privateKey", privateKey);
// 공개키를 가수부와 지수부로 나눠야함
// 공개키(Double Type) : 가수부 + 지수부 => 클라이언트에 제공
// ex) -143.12344556
// float(32bit 단정도 소수) : 부호비트 1bit(양수 0 | 음수 1) + 지수 8bit(소숫점 자리수) + 가수 23bit(실수 표현)
// double(64bit 배정도 소수) : 부호비트 1bit(양수 0 | 음수 1) + 지수 11bit(소숫점 자리수) + 가수 52bit(실수 표현)
// 공개키가 어떤 알고리즘으로 되어있는지 설정
keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = (RSAPublicKeySpec)keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
// 공개키 가수부
String publicModulus = publicKeySpec.getModulus().toString(16);
// 공개키 지수부
String publicExponent = publicKeySpec.getPublicExponent().toString(16);
publicKeyMap.put("publicModulus", publicModulus);
publicKeyMap.put("publicExponent", publicExponent);
}catch(NoSuchAlgorithmException e1){
e1.printStackTrace();
}catch(InvalidKeySpecException e2){
e2.printStackTrace();
}
return publicKeyMap;
}
//암호문을 평문으로 복호화
public static String decryptRSA(HttpSession session, String secureValue){
String returnValue = "";
PrivateKey privateKey = (PrivateKey) session.getAttribute("pirvateKey");
try{
// 평문으로 바꿀 때?
Cipher cipher = Cipher.getInstance("RSA"); //암호문이 어떤 암호화 방식으로 전달되었는지
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 암호문은 짝수 단위로 바이너리 코드로 존재
// hextoByteArray라는 메소드는 직접 만들어야함
byte[] targetByte = hextoByteArray(secureValue);
byte[] beforeString = cipher.doFinal(targetByte);
returnValue = new String(beforeString, "UTF-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return returnValue;
}
private static byte[] hextoByteArray(String secureValue) {
// 암호문이 null이거나 짝수가 아니면 암호문이 암호화 에러.
if(secureValue == null || secureValue.length()%2 != 0){
return new byte[]{};
}
byte[] bytes = new byte[secureValue.length()/2];
for(int i = 0; i < secureValue.length(); i+=2){
byte value = (byte)Integer.parseInt(secureValue.substring(i,i+2), 16);
bytes[(int)Math.floor(i/2)] = value;
}
return bytes;
}
}
jsp에서 사용 예제
필요 js 넣기
www-cs-students.stanford.edu/~tjw/jsbn/
[loginForm.jsp]
<%@page import="java.util.Map"%>
<%@page import="kr.or.ddit.utiles.CryptoGenerator"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
// 요청할 때마다 공개키, 비밀키가 새로 만들어짐 세션에 계속 갱신
Map<String, String> publicKeyMap = CryptoGenerator.generatePairKey(session);
//pageContext.setAttribute("publicKeyMap", publicKeyMap); 한거나 마찬가지?
%>
<c:url var ="loginCheckURL" value="/12/loginCheck.jsp"></c:url>
<c:set var="publicKeyMap" value ="<%=publicKeyMap %>"></c:set>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="${pageContext.request.contextPath }/css/admin.css" type="text/css">
<script type='text/javascript' src='http://code.jquery.com/jquery-latest.js'></script>
<script type = "text/javascript" src ="${pageContext.request.contextPath}/js/common/validation.js "></script>
<script type='text/javascript' src="${pageContext.request.contextPath }/js/common/cookieControl.js"></script>
<script type ="text/javascript" src ="${pageContext.request.contextPath }/js/crypto/jsbn.js"></script>
<script type ="text/javascript" src ="${pageContext.request.contextPath }/js/crypto/rsa.js"></script>
<script type ="text/javascript" src ="${pageContext.request.contextPath }/js/crypto/prng4.js"></script>
<script type ="text/javascript" src ="${pageContext.request.contextPath }/js/crypto/rng.js"></script>
<title>회원관리 관리자 로그인</title>
</head>
<!--
자바스크립트 : RSA 암호화 알고리즘 처리
1. js 라이브러리
http://www-cs-students.stanford.edu/~tjw/jsbn/
jsbn.js, prng4.jg, rng.js, rsa,js 다운로드
2. WebContext 하위에 배치
3. 취득한 js파일 import 우선순위 (순서대로)
3.1 jsbn.js
3.2 rsa.js
3.3 prng4.js
3.4 rng.js
-->
<body>
<table width="770" border="0" align="center" cellpadding="0"
cellspacing="0" style="margin: 90px;">
<tr>
<td height="150" align="center"><img src="${pageContext.request.contextPath }/image/p_login.gif" /></td>
</tr>
<tr>
<td height="174"
style="background: url(${pageContext.request.contextPath }/image/login_bg.jpg); border: 1px solid #e3e3e3;">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="260" height="110" align="center"
style="border-right: 1px dotted #736357;">
<img src="${pageContext.request.contextPath }/image/logo.jpg" />
</td>
<td>
<table border="0" align="center" cellpadding="5"
cellspacing="0">
<tr>
<td><b>아이디</b></td>
<td><input type="text" name="mem_id" class="box" tabindex="3" height="18" /></td>
<td rowspan="2">
<img src="${pageContext.request.contextPath }/image/login.gif" class="loginBtn"/>
</td>
</tr>
<tr>
<td><b>패스워드</b></td>
<td><input type="password" name="mem_pass" class="box" tabindex="3" height="18" /></td>
</tr>
<tr>
<td colspan="3" align="right"><a href="">회원가입을 원하세요??</a></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
<script type = "text/javascript">
$(function(){
if('${!empty param.message}'){
alert("${param.message}");
}
$('.loginBtn').click(function(){
var mem_id = $('input[name=mem_id]').val();
var mem_pass = $('input[name=mem_pass]').val();
if(!mem_id.validationID()){
alert("아이디를 바르게 입력해주세요");
$('input[name=mem_id]').focus();
return false;
}
if(!mem_pass.validationPWD()){
alert("비밀번호를 바르게 입력해주세요");
$('input[name=mem_pass]').focus();
return false;
}
var modulus = '${publicKeyMap["publicModulus"]}';
var exponent = '${publicKeyMap["publicExponent"]}';
var rsaObject = new RSAKey();
rsaObject.setPublic(modulus,exponent);
//평문을 암호문으로 바꾸기
var encryptID = rsaObject.encrypt($('input[name_mem_id]').val());
var encryptPWD = rsaObject.encrypt($('input[name_mem_pass]').val());
var $frm = $('<form action="${loginCheckURL}" method="post"></form>');
var $inputID = $('<input type="hidden" value="' +encryptID+ '" name="mem_id" />');
var $inputPWD = $('<input type="hidden" value="' +encryptPWD+ '" name="mem_pass" />');
$frm.append($inputID);
$frm.append($inputPWD);
$(document.body).append($frm);
$frm.submit();
});
});
</script>
</html>