JSP

JSP(사용자 정보 암호화 / 복호화 처리)

행복하게사는게꿈 2020. 6. 18. 15:03

사용자 정보 암호화 / 복호화 처리

 

 암호화 종류

 

  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>