package itn.let.uat.uia.web; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Random; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.springframework.stereotype.Component; import org.springframework.ui.ModelMap; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMapManager; import org.springframework.web.servlet.support.RequestContextUtils; import com.icert.comm.secu.IcertSecuManager; import itn.let.cert.phone.service.CertPhoneService; import itn.let.cert.phone.service.MberCertPhoneVO; import itn.let.mjo.pay.service.KmcVO; import itn.let.uat.uia.service.AuthCertVO; @Component("KmcCertChecker") public class KmcCertChecker { @Resource(name = "CertPhoneService") private CertPhoneService certPhoneService; //회원가입 시 인증수단을 휴대폰 본인인증만 했을 경우 사용 public AuthCertVO authCertCheck( MberCertPhoneVO mberCertPhoneVO , HttpServletRequest request ) { //url 세팅 mberCertPhoneVO = setMberCertPhoneVO(request, mberCertPhoneVO); //kmc step 01 데이터 //reqNum은 최대 40byte 까지 사용 가능 String reqNum = getDay() + getRanNum(); //요청번호 String tr_cert = ""; String cpId = "MJOM1001"; // 회원사ID String urlCode = mberCertPhoneVO.getUrlCode(); // URL코드 String certNum = reqNum; // 요청번호 ( 본인인증 요청시 중복되지 않게 생성해야함. (예-시퀀스번호) ) String date = getDay(); // 요청일시 String certMet = "M"; // 본인인증방법 - M:휴대폰 본인인증, C:신용카드인증, P:공인인증서 인증 // String name = ""; // 성명 String name = mberCertPhoneVO.getName(); // 성명 String phoneNo = mberCertPhoneVO.getPhoneNo(); // 휴대폰번호 String phoneCorp = mberCertPhoneVO.getPhoneCorp(); // 이동통신사 if(phoneCorp == null) phoneCorp = ""; String birthDay = mberCertPhoneVO.getBirthDay(); // 생년월일 String gender = mberCertPhoneVO.getGender(); // 성별 if(gender == null) gender = ""; String nation = mberCertPhoneVO.getNation(); // 내외국인 구분 - 0:내국인, 1:외국인 String plusInfo = mberCertPhoneVO.getPlusInfo(); // 추가DATA정보 String extendVar = "0000000000000000"; // 확장변수 //End-tr_cert 데이터 변수 선언 --------------------------------------------------------------- String tr_url = getDomain(request) + mberCertPhoneVO.getTrUrl();// // 본인인증서비스 결과수신 POPUP URL String tr_add = "N"; // IFrame사용여부 //01. 한국모바일인증(주) 암호화 모듈 선언 // IcertSecuManager seed = new IcertSecuManager(); com.icert.comm.secu.IcertSecuManager seed = new com.icert.comm.secu.IcertSecuManager(); //02. 1차 암호화 (tr_cert 데이터변수 조합 후 암호화) String enc_tr_cert = ""; // tr_cert = cpId +"/"+ urlCode +"/"+ certNum +"/"+ date +"/"+ certMet +"/"+ birthDay +"/"+ gender +"/"+ name +"/"+ phoneNo +"/"+ phoneCorp +"/"+ nation +"/"+ plusInfo +"/"+ extendVar; tr_cert = cpId +"/"+ urlCode +"/"+ certNum +"/"+ date +"/"+ certMet +"///////"+ plusInfo +"/"+ extendVar; enc_tr_cert = seed.getEnc(tr_cert, ""); //03. 1차 암호화 데이터에 대한 위변조 검증값 생성 (HMAC) String hmacMsg = ""; hmacMsg = seed.getMsg(enc_tr_cert); //04. 2차 암호화 (1차 암호화 데이터, HMAC 데이터, extendVar 조합 후 암호화) tr_cert = seed.getEnc(enc_tr_cert + "/" + hmacMsg + "/" + extendVar, ""); AuthCertVO authCertVO = new AuthCertVO(); authCertVO.setTr_cert(tr_cert); authCertVO.setTr_url(tr_url); authCertVO.setTr_add(tr_add); return authCertVO; } public KmcVO authCertResult( HttpServletRequest request , HttpServletResponse response , ModelMap model ) throws IOException { //크롬 SameSite정책 방지 - 도메인이 다른 타사로 이동 시 크롬 정책에 의해 세션 유실이 일어나는 경우가 있는데, 이를 방지하기 위해 samesite 보안을 none처리 response.setHeader("Set-Cookie", "mberSession=mberSession; Secure; SameSite=None"); KmcVO kmcVO = new KmcVO(); //return VO // 변수 ------------------------------------------------------------------------------------------------------------- String api_token = ""; // 토큰값(암호화) String api_certNum = ""; // 요청번호(암호화) String message = ""; // JSON 전문 String result_cd = ""; // JSON 결과코드 String result_msg = ""; // JSON 결과-상세 String strResult = ""; // JSON 결과 String apiRecCert = ""; // JSON 전송 데이터 String apiCertNum = ""; // JSON 전송 데이터 String rec_cert = ""; // 결과수신DATA String k_certNum = ""; // 파라미터로 수신한 요청번호 String certNum = ""; // 요청번호 String date = ""; // 요청일시 String CI = ""; // 연계정보(CI) String DI = ""; // 중복가입확인정보(DI) String phoneNo = ""; // 휴대폰번호 String phoneCorp = ""; // 이동통신사 String birth = ""; // 생년월일 String gender = ""; // 성별 String nation = ""; // 내국인 String name = ""; // 성명 String reserve1 = ""; // 예비필드 String reserve2 = ""; // 예비필드 String reserve3 = ""; // 예비필드 String reserve4 = ""; // 예비필드 String result = ""; // 결과값 String certMet = ""; // 인증방법 String ip = ""; // ip주소 String plusInfo = ""; String encPara = ""; String encMsg1 = ""; String encMsg2 = ""; String msgChk = ""; //----------------------------------------------------------------------------------------------------------------- try{ // Parameter 수신 -------------------------------------------------------------------- api_token = request.getParameter("apiToken").trim(); api_certNum = request.getParameter("certNum"); // 파라미터 유효성 검증 if( api_token.length() == 0 ){ goErrorPage("토큰값 비정상", request, response); return kmcVO; } if( api_certNum.length() == 0 ){ goErrorPage("요청번호 비정상", request, response); return kmcVO; } //현재시각 세팅(YYYYMMDDHI24MISS) Calendar today = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String api_date = sdf.format(today.getTime()); //01. 암호화 모듈 (jar) Loading com.icert.comm.secu.IcertSecuManager seed = new com.icert.comm.secu.IcertSecuManager(); //api_token, api_certNum 복호화 api_token = seed.getDec(api_token, ""); api_certNum = seed.getDec(api_certNum, ""); // 파라미터 유효성 검증 if( api_token.length() == 0 ){ goErrorPage("토큰값 비정상(복호화 후)", request, response); return kmcVO; } if( api_certNum.length() == 0 ){ goErrorPage("요청번호 비정상(복호화 후)", request, response); return kmcVO; } // 1. URL 설정 String serverURL = "https://www.kmcert.com/kmcis/api/kmcisToken_api.jsp"; // 2. 연결 생성 URL url = new URL(serverURL); // 3. HttpURLConnection 객체 생성. HttpURLConnection con = null; OutputStream wr = null; BufferedReader bufferedReader = null; // 4. URL 연결 (웹페이지 URL 연결.) con = (HttpURLConnection)url.openConnection(); con.setConnectTimeout(20000); // TimeOut 시간 (서버 접속시 연결 시간 - 20초) con.setReadTimeout(20000); // TimeOut 시간 (Read시 연결 시간 - 20초) con.setDoOutput(true); // OutputStream으로 POST 데이터를 넘겨주겠다는 옵션. con.setRequestProperty("Content-Type", "application/json;charset=utf-8"); // 타입설정(application/json) 형식으로 전송 (Request Body 전달시 application/json로 서버에 전달.) con.setRequestProperty("Accept", "application/json"); // 서버 Response Data를 JSON 형식의 타입으로 요청. con.setRequestMethod("POST"); // 요청 방식 선택 (POST) // 5. JSON 전문 구성 JSONObject jsonData = new JSONObject(); jsonData.put("apiToken", api_token); jsonData.put("apiDate", api_date); message = jsonData.toString(); // 6. 전송 // Request Body에 Data를 담기위해 OutputStream 객체를 생성. wr = con.getOutputStream(); // Request Body에 Data 셋팅.(한글깨짐 방지를 위해 utf-8인코딩 처리 wr.write(message.getBytes("utf-8")); wr.flush(); wr.close(); // 실제 서버로 Request 요청 하는 부분. (응답 코드를 받는다. 200 성공, 나머지 에러) int responseCode = con.getResponseCode(); // 4. 결과 수신 if(responseCode == 200){ bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8")); }else{ bufferedReader = new BufferedReader(new InputStreamReader(con.getErrorStream(), "UTF-8")); } if(bufferedReader != null){ StringBuilder stringBuilder = new StringBuilder(); String line = ""; while((line = bufferedReader.readLine()) != null){ stringBuilder.append(line); } bufferedReader.close(); String stringRet = stringBuilder.toString(); // 5. 넘어온 문자열을 JSON 객체로 변환 JSONParser jsonParser = new JSONParser(); //JSON데이터를 넣어 JSON Object 로 만들어 준다. JSONObject jsonObj = (JSONObject)jsonParser.parse(stringRet); // 6. JSON 객체에서 데이터 가져오기 if(jsonObj.get("result_cd") != null){ result_cd = jsonObj.get("result_cd").toString(); if("APR01".equals(result_cd)){ //통신성공 strResult = "Y"; rec_cert = jsonObj.get("apiRecCert").toString(); k_certNum = jsonObj.get("apiCertNum").toString(); }else if("APR02".equals(result_cd)){ //실패 - Token Expire strResult = "N"; result_msg = "실패 - Token Expire"; }else if("APR03".equals(result_cd)){ //실패 - Token Not Found strResult = "N"; result_msg = "실패 - Token Not Found"; }else if("APR04".equals(result_cd)){ //실패 - API 요청일시 길이 오류 strResult = "N"; result_msg = "실패 - API 요청일시 길이 오류"; }else if("APR05".equals(result_cd)){ //실패 - API 토큰 길이 오류 strResult = "N"; result_msg = "실패 - API 토큰 길이 오류"; }else if("APR06".equals(result_cd)){ //실패 - 결과전송 재요청(3회 제한) strResult = "N"; result_msg = "실패 - 결과전송 재요청(3회 제한)"; } }else{ //JSON 결과코드 에러 strResult = "F"; } }else{ //timeout except 처리 strResult = "F"; } // 파라미터 유효성 검증 if(!strResult.equals("Y")){ goErrorPage("결과값 비정상, 결과코드["+result_cd+"], "+"상세내용["+result_msg+"]", request, response); return kmcVO; } //02. 1차 복호화 rec_cert = seed.getDec(rec_cert, k_certNum); //03. 1차 파싱 int inf1 = rec_cert.indexOf("/",0); int inf2 = rec_cert.indexOf("/",inf1+1); encPara = rec_cert.substring(0,inf1); //암호화된 통합 파라미터 encMsg1 = rec_cert.substring(inf1+1,inf2); //암호화된 통합 파라미터의 Hash값 //04. 위변조 검증 encMsg2 = seed.getMsg(encPara); if(encMsg2.equals(encMsg1)){ msgChk="Y"; } if(msgChk.equals("N")){ goErrorPage("비정상적인 접근입니다.!!", request, response); return kmcVO; } //05. 2차 복호화 rec_cert = seed.getDec(encPara, ""); //06. 2차 파싱 int info1 = rec_cert.indexOf("/",0); int info2 = rec_cert.indexOf("/",info1+1); int info3 = rec_cert.indexOf("/",info2+1); int info4 = rec_cert.indexOf("/",info3+1); int info5 = rec_cert.indexOf("/",info4+1); int info6 = rec_cert.indexOf("/",info5+1); int info7 = rec_cert.indexOf("/",info6+1); int info8 = rec_cert.indexOf("/",info7+1); int info9 = rec_cert.indexOf("/",info8+1); int info10 = rec_cert.indexOf("/",info9+1); int info11 = rec_cert.indexOf("/",info10+1); int info12 = rec_cert.indexOf("/",info11+1); int info13 = rec_cert.indexOf("/",info12+1); int info14 = rec_cert.indexOf("/",info13+1); int info15 = rec_cert.indexOf("/",info14+1); int info16 = rec_cert.indexOf("/",info15+1); int info17 = rec_cert.indexOf("/",info16+1); int info18 = rec_cert.indexOf("/",info17+1); certNum = rec_cert.substring(0,info1); date = rec_cert.substring(info1+1,info2); CI = rec_cert.substring(info2+1,info3); phoneNo = rec_cert.substring(info3+1,info4); phoneCorp = rec_cert.substring(info4+1,info5); birth = rec_cert.substring(info5+1,info6); gender = rec_cert.substring(info6+1,info7); nation = rec_cert.substring(info7+1,info8); name = rec_cert.substring(info8+1,info9); result = rec_cert.substring(info9+1,info10); certMet = rec_cert.substring(info10+1,info11); ip = rec_cert.substring(info11+1,info12); reserve1 = rec_cert.substring(info12+1,info13); reserve2 = rec_cert.substring(info13+1,info14); reserve3 = rec_cert.substring(info14+1,info15); reserve4 = rec_cert.substring(info15+1,info16); plusInfo = rec_cert.substring(info16+1,info17); DI = rec_cert.substring(info17+1,info18); //07. CI, DI 복호화 CI = seed.getDec(CI, ""); DI = seed.getDec(DI, ""); // ---------------------------------------------------------------------------------- }catch(StringIndexOutOfBoundsException ex){ goErrorPage("StringIndexOutOfBoundsException", request, response); }catch(NullPointerException ex){ goErrorPage("NullPointerException", request, response); }catch(NumberFormatException ex){ goErrorPage("NumberFormatException", request, response); }catch(IllegalStateException ex){ goErrorPage("IllegalStateException", request, response); }catch(IndexOutOfBoundsException ex){ goErrorPage("IndexOutOfBoundsException", request, response); } catch (IOException e) { goErrorPage("IOException", request, response); } catch (ParseException e) { goErrorPage("ParseException", request, response); } return kmcVO; } private String getDomain(HttpServletRequest request) { String serverNm = request.getScheme() + "://" + request.getServerName(); if(request.getServerPort() != 80 && request.getServerPort() != 443) { serverNm += ":" + request.getServerPort(); } return serverNm; } private String getDay() { Calendar today = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String day = sdf.format(today.getTime()); return day; } private String getRanNum() { Random ran = new Random(); //랜덤 문자 길이 int numLength = 6; String randomStr = ""; for (int i = 0; i < numLength; i++) { //0 ~ 9 랜덤 숫자 생성 randomStr += ran.nextInt(10); } return randomStr; } private MberCertPhoneVO setMberCertPhoneVO( HttpServletRequest request , MberCertPhoneVO mberCertPhoneVO ) { mberCertPhoneVO.setUrl(mberCertPhoneVO.getTrUrl()); mberCertPhoneVO.setHost(getDomain(request)); MberCertPhoneVO tmpMberCertPhoneVO = new MberCertPhoneVO(); try { tmpMberCertPhoneVO = certPhoneService.selectCertUrlCode(mberCertPhoneVO); } catch (Exception e) { System.out.println(e.getMessage()); } if(tmpMberCertPhoneVO != null) { mberCertPhoneVO.setUrl(tmpMberCertPhoneVO.getUrl()); mberCertPhoneVO.setHost(tmpMberCertPhoneVO.getHost()); mberCertPhoneVO.setUrlCode(tmpMberCertPhoneVO.getUrlCode()); } return mberCertPhoneVO; } private void goErrorPage(String msg, HttpServletRequest request, HttpServletResponse response) throws IOException { FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); flashMap.put("msg", msg); FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); flashMapManager.saveOutputFlashMap(flashMap, request, response); response.sendRedirect("/web/cert/log/kmcErrorPage.do"); } }