2008년 12월 25일 목요일

삼성생명 보험계약종합안내






아래 내용으로 삼성생명에 메일을 보냈더니

담당자가 직접 수정한 메일을 보냈습니다.

그리고 앞으로 될때까지 직접 챙기겠다고 합니다.

담당자 메일을 웹사이트 접속이 불가하여

인터넷 검색엔진에서 이용자 약관내용을 찾았고 그 안의 담당자 메일이 있어 거길로 발송하였습니다.

-----------

삼성생명 보험계약종합안내 메일 오류 관련 개선 요청


안녕하세요.
저는 삼성생명 고객인 이*산입니다

IT산업에 종사하고 있고
주로 Mac OSX에서 Firefox browser를 사용합니다.

최근 삼성생명에서 발송하는 보험계약종합안내 메일에서
파이어폭스나 리눅스 유저를 고려하여 일반메일 재발행 신청하기라는 기능이 추가되었는데

아래와 같은 문제가 있어서 자세하게 안내해드립니다.

기왕 파이어폭스나 리눅스, 매킨토시 유저를 지원하기로 하였으면
유저입장에서 정확하게 동작하는지 확인후 안내하고 실제로 동작할 수 있도록 조치해주십시오.

1. 아래 그림처럼 삼성생명에서 메일을 받았습니다.

2. 저는 Mac OSX에서 파이어폭스를 주로 사용하고 있는데
메일 내용중 "보안프로그램(Active-X)은 일부 운영체계(윈도우비스타, 파이어폭스, 리눅스, 매킨토시 등) 및 일부 회사 메일의 경우 첨부파일이 개봉되지 않을 수 있으니 아래 [일반메일 재발행 신청하기]에서 일반메일을 신청하시기 바랍니다"라고 하는 안내문구가 보여서 [일반메일 재발행 신청하기] 링크를 클릭하였습니다.
몇단계 확인 alert 창이 뜬 이후 일반메일 신청 화면이 아래 그림처럼 나옵니다.

3. 주민번호 뒤번호 7자리를 입력하면 아무런 반응이 없습니다.
혹시나해서 Firefox의 JavaScript 오류 정보 창을 열어보니 아래와 같은 사이트 오류가 확인되었습니다.

4. 혹시나 해서 [에러 발생시 신청방법] 링크를 클릭하면 아래 그림처럼 ActiveX 설치를 시도하고 다음 단계로 진행을 할 수 없습니다.

2008년 12월 18일 목요일

PCI DSS Guide관련 블로그 오픈

PCI DSS 안내하는 블로그를 오픈하였습니다.
http://pcidssguide.blogspot.com

살펴보세요

웹표준과 지불결제

웹표준과 지불결제

1. 웹표준

1.1 전세계 브라우저 점유율 (by NetApplications)



* MSIE점유율은 줄어들고 있고 Firefox는 상승중

1.2 대한민국 브라우저 점유율 종합 (by NetApplications)

* 국내 MSIE점유율 종합은 96.59%(4월), 95.65%(5월)

1.3 지불결제가 필요한 쇼핑분야 점유율(by internettrend)


* 쇼핑분야는 국가평균보다 점유율이 더 높음

* 기간별 변화율 추이는 점차 줄어들고 있음.


1.4 국가별 MSIE Market Share 비율 (by NetApplications)

* 오차율을 감안한다면 "South Korea"는 세계 최고의 MSIE Market Share 비율을 보이고 있음.

1.5 페이게이트 결제를 사용하는 특정 업체의 브라우저 점유율 (by analytics.google.com)

누적5월까지
누적6월까지

1.6 웹표준 이전과 이후





웹표준 이전

웹표준 이후

유저환경에 대한 기술적 별도 대응 - IPTV환경 따로 - 모바일환경 따로 - PC환경 따로 동일한 하나의 기술로 모든 시장환경에 대응 PC, 모바일, IPTV 환경의 구분 불필요

1.7 웹표준은 돈이되는 기술






















구분

웹표준 미준수업체

웹표준 준수업체

MSIE PC Market (95%) 유저의 95% 수용, but 시장이 점점 줄어들고 있음. (좌동)
Non-MSIE PC Market (5%) X 숨어있는 새로운 5% 유저 수용, 매출 5% 증대 가능성
Mobile Full Browsing Market (00%) X 무한 가능성 가진 새로운 유저층 수용, 매출 증대 가능성 가늠불가
IPTV Setup Market (00%) X 무한 가능성 가진 새로운 유저층 수용, 매출증대 가느성 가늠불가
냉장고에 붙은 embeded device X 숨어있는 새로운 유저 수용, 매출증대 가능성
기타 웹표준 순수 device(iphone, PSP, XBox...) X 숨어있는 새오운 유저 수용, 매출증대 가능성

1.8 Mobile Device에서 결제 demo


1.9 지불결제분야의 웹표준 활성화를 위한 요건







사업적

매우 매력적

기술적

제약 없음.

제도적

공인인증서 의무사용 개인보안프로그램 의무사용 기타등등.

2. 지불결제 및 보안에 대한 이해

2.1 보안방식의 변화 필요





현재

희망사항

보안당국이 국민 개개인의 보안을 모두 책임지려는 불가능한 자세
국가가 전국민을 보호
개인의 선택권(자율성, 권한)을 존중하는 방향으로 개선 필요 국가의 보호도 필요하지만 내 PC를 내가 보호할 수 있도록 개인의 선택권 존중.
국가가 전국민을 보호 + 자신을 내가 추가로 보호
* 전자금융거래법 시행세칙 29조 2항 3절 : 전자금융업자에게 국민 개개인의 이용자 PC에 프로그램을 설치하도록 요구함. 본질적으로 이용자의 동의 및 허용이 있어야 가능한 조항


2.2 사용자에 대한 관점 변화 필요





현행

희망사항

사용자는 무지하고 피동적이고 잠재적인 범죄자이다. 사용자는 똑똑하고 능동적이고 선량하다 ==> 개인의 선택권 존중해야함

2.3 전자금융거래와 전자서명법의 공인인증서










구분

전자금융거래법

전자서명법

공인 전자서명/서명검증 요구됨 요구됨
데이터 암복호화 요구됨 요구되지 않음.
==> 데이터 암복호화와 서명 /서명검증의 명확한 분리 필요 ==> 현재는 전자금융거래시 사용되는 공인인증서가 사실상 데이터 암복호화를 포함하여 사용.

3. 전자서명법과 공인인증서

3.1 인감제도와 공인인증서 비교

























구분

인감제도

공인인증서

취지 오프라인에서 국민 누구든지 자유롭게 상대방의 신원 및 의사 확인 온라인에서 국민 누구든지 자유롭게 상대방의 신원 및 의사 확인
본인의 표시 인감도장 공인인증서 Private Key
본인의 표시에 대한 제3자 증명 인감증명서 공인인증서
본인의사 표시 인감 날인 공인 전자서명
신원확인 인감증명서 대조 공인전자서명 서명검증
누구나 자유롭게 날인하고 대조 서명하고 검증 불가능
어떠한 영역에 대해서도 적용 불가능 (금융거래등에만 제한적 사용)

3.2 전자서명법상의 공인인증기관의 의무

- 전자서명 가입자 설비 제공 의무 : 전자서명 생성을 위한 소프트웨어 제공 - 서명검증을 위한 쉬운 수단 제공 의무 : 전자서명 검증을 위한 쉬운 수단 제공

3.3 공인인증기관이 가입자 설비를 제공하면 기존 보안업체들은 모두 다 망하는가?







현재

희망사항

아주 많은 업체들(공인인증기관 포함)이 기초적인 공인인증서 전자서명, 서명검증 소프트웨어 반복하여 제작 => 리소스 낭비 6개 공인인증기관이 검증된 가입자 설비 제공, 기존보안업체들은 좀더 창의적인 업무에 집중가능(데이터 암복호화, 키관리, 기타 응용 서비스 분야) 기존 보안업체뿐만 아니라 다양한 사업체들이 참여로 인한 활성화
공인전자서명/서명검증을 할 수 있는 소프트웨어를 만들기 위해서는 돈이 많거나 기술이 뛰어나거나 시간이 아주 많아야함. 구멍가게 쇼핑몰 아저씨도 이용가능

4. 웹표준 지불결제처리를 위한 기술적/제도적 대안

4.1 주요 이슈 및 대안 요약













구분

현행

대안

키보드보안 ActiveX Control 자동설치 OnScreen Keyboard
개인보안프로그램(방화벽, 안티바이러스) ActiveX Control 자동설치 유저가 능동적으로 사전 설치, 기설치 유저는 추가설치 없이 진행허용
공인인증서 ActiveX Control 가입자설비 - 사용안함(예외규정 준용) - Java Applet (KLDP OpenSigner) - XPCOM (전자정부) - 신용카드 금액인증

4.2 키보드보안 방안

- 유저가 원하는 경우 사용하지 않고 진행가능하도록 선택권 부여해야함. (시행세칙 29조 기 명시) - Auto Form Filler 활성화 - OnScreen keyboard : 광범위하게 사용되는 검증된 방식, 국내 다수 금융기관에서 이용 - Smart Card등 PKCS#11 device이용한 보완 - 기타 : http://en.wikipedia.org/wiki/Keystroke_logging 참조 *국민은행 온스크린 키보드 보안 적용 화면

4.3 개인보안프로그램

- 유저가 원하는 경우 사용하지 않고 진행가능하도록 선택권 부여해야함. (시행세칙 29조 기 명시) - 유저가 능동적으로 사전 설치할 수 있도록 프로그램 목록 제시 * 페이게이트의 경우

4.4 공인인증서 사용예외 (시행세칙 31조)

- 본인 계좌에 대한 조회 업무 - 전화, CD/ATM 등과 같이 공인인증서의 설치·운용이 불가능한 수단을 이용한 전자금융거래 - 등록금, 원서접수비 등 본인확인이 가능하고 입금계좌가 지정되어 있는 경우 - 전자상거래에서 지급결제로서 30만원 미만의 신용카드 결제 또는 온라인 계좌이체 - 전자화폐, 선불전자지급수단을 온라인상에서 사용하는 경우 - 금융기관 등이 범위를 정하여 공인인증서 적용을 제외할 것을 감독원장에게 요청하고 감독원장이 이를 승인하는 경우

4.5 신용카드 금액인증

- 이용자(페이게이트도 이용자임)의 자유로운 창의력을 저해하지 않았을때 도출될 수 있는 결과물의 한 예.
- 시행세칙 31조의 "본인 계좌에 대한 조회업무"에 대한 공인인증서 적용예외 규정을 기반으로 공인인증서를 사용하지 않음.
- 공인인증서 미사용시 발생할 수 있는 신원확인 요구사항을 "신용카드 금액인증"이라는 기술로 보완


[신용카드 금액인증 50만원 결제예시] 1) 서버에서 1회용 비밀번호 생성로직으로 난수 발생하여 신용카드 거래 1차승인 (237698원) 2) 승인금액을 고객에게 안내하지는 않음. 3) 고객은 자신의 카드발행사에서 승인금액이 얼마인지 확인 (다양한 확인방식 병행, SMS문자, 웹사이트조회, 팩스조회, 전화조회, 방문조회 등) 4) 승인금액을 인증코드로 입력하면 서버에서 1차 승인금액과 비교 5) 나머지 잔액 추가승인하여 결제 완료
신용카드 금액인증에 관한 보다 자세한 내용은 아래 URL 참조
http://docs.google.com/a/paygate.net/Doc?id=dhm28v4q_1631cd5kj9dk


4.6 ActiveX Control이용방식 개선

- ActiveX Control 사용이 꼭 필요한 경우가 여전히 존재함

- 꼭 ActiveX를 사용해야한다면 아래와 같이 사용방식을 개선







현행 개선
- Page Head에 걸어버리거나 <html> <head> <OBJECT ....>...</OBJECT> </head> - 사이트 전역에 반영되는 Hidden frame에 넣어버리면 IE가 아닌 브라우저들은 접근자체가 불가해짐. - ActiveX가 꼭 필요할때만 DIV tag 안에서 선언하여 이용. <div id=forActiveX></div> document.getElementById('forActiveX').innerHTML = '<OBJECT...>...</OBJECT>'; - 이용후에는 Div tag내용 삭제 document.getElementById('forActiveX').innerHTML = '';
ActiveX를 이용하여 Client와 Server간의 세션 보호 SSL이용

2008년 12월 17일 수요일

Latest Browser MarketShare






최신 브라우저 마켓쉐어


netapplications 유료 계정 만료가 얼마안남아서
최신 브라우저 마켓쉐어 자료를 뽑아보았습니다.

자료 발췌시기 : 2008년 12월 18일

1. 전세계 브라우저 마켓쉐어

MSIE가 69%입니다.
Firefox가 20%를 넘어섰고 Safari도 많이 늘었군요.

2. 대한민국 브라우저 마켓쉐어

MSIE가 90%로 줄었습니다.

3. 전세계 브라우저 점유율 변화추이입니다.

IE는 계속 떨어지고 다른 브라우저는 계속 올라가는군요.
국가별 추이도 뽑아볼수 있으면 좋은데 그건 아직 없습니다.

4. 전세계 MSIE 국가별 점유율 순위입니다

대한민국이 드디어 소말리아에게 MSIE 점유율 1위를 내줬습니다.(ㅠㅠ)


5. 전세계 OS 통계입니다.


2008년 6월 18일 수요일

CreditCard Amount Authentication

신용카드 금액인증.


1. Basic Concept

  • Credit Card Amount Authentication authenticates card holder with the approval amount.
  • The AA(Amount Authentication) server generate random unique amount (AA code) and get the approval with it.
  • The card holder have to find the approved amount from his card issuer
  • If the amount (submitted from card holder) is same to AA code, the authentiation is completed.

2. Basic Concept Flow



2.1 Basic Concept Flow2



3. Benefit


  • It’s Easy and Strong


    • It is very easy to understand and implement
    • But it’s also strong

  • Issuer independent


    • AA mechanism covers 100% card issuers
    • even if they are not yet ready.

  • Platform independent


    • With the minimal authentication value (Amount)
    • AA works on any platforms (PC, Mac, Wireless Phone, etc)

  • Fraud free


    • AA mechanism is strong enough to down fraud rate to 0%

4. General Information about AA

  • AA operator have to meet the PCIDSS compliant
  • Initiated from PayGate Inc.
    • PayGate Inc., Seoul, Korea
      www.paygate.net
  • If any questions, send mail to Mountie Lee (mountie@paygate.net)

2008년 1월 30일 수요일

Cross-Domain 문제 해결 방법

by 이동산 — 최종 수정일: 2006-04-25 10:53

Cross-Domain 문제 해결방법에 대한 조사결과

AJAX를 살펴보면서 Cross-Domain 제한의 문제점에 봉착함.

Cross-Domain의 Pattern은 아래 3가지로 나눠볼 수 있음.

1. from one single domain to many targets

2. from many sources to one single target

3. from many sources to many targets.

지금 고민하는것은 2번항목임.

[지금까지 찾은 해결방법]

(1) Proxy Server 이용 : 최초 시도한 방법인데 이건 사실상 위 1번 항목에 해당하는것으로 적당하지 않고 또한 cookie나 session문제등이 있어서 몇번 사용해보다고 취소함.

(2) SignedJavaScript : Netscape계열에서만 되며 JavaScript에 대한 전자서명시 서명된 javascript는 cross-domain 제약을 극복하여 자유롭게 다른 사이트와 통신가능함. MSIE에서는 동작하지 않는다는 문제점이 있고 아직 테스트해보지 못함.

(3) Flash4AJAX : 지금까지 찾은 최상의 방법인데 ActiveX/Plugin을 사용하기는 하지만 광범위하게 배포된 Flash를 이용하여 구현함으로 Cross-Browser문제는 쉽게 극복가능함. 그러나 Flash에 대해서 잘 모르기 때문에 좀더 살펴보고 있음.

(4) JSON + WebServices : 야후 API에서 사용하고 있는데 Target서버에서 JSON Format으로 Output을 뿌려줘야하는 문제 있음. 아직 테스트 못함.

(5) ActiveX : 어떻게 보면 가장 손쉬운 방법인데 일단 하나 만들어 사용중이기는 하지만 Cross-Browser 문제해결에는 적절치 못함.

(6) Windows TrustedZone 등록 : Source Domain상의 유저가 해당 Source Domain을 Windows TrustedZone에 등록해버리면 어디든 나갈 수 있는데 사실 말이 안됨. 보안상 엄청 위험하기도함.

(7) IFrame 이용 : iframe이용하면 일단 AJAX Cross-Domain 문제는 해결되지만 frame간의 cross-domain 문제가 새롭게 대두됨. cross-domain cookie등 Security issue가 꾸준하게 제기되어온 사항임. 일단 target server로 Get Method로 데이터를 넘기기만 하는 형태로는 쓸만함. (결과수신은 아주 어려움)

[ 최종 선택한 방법 : JSON ]

JSON Format으로 결과 전달하고 Callback method로 해당 결과 수신하는 방법이 정답임.

우선 단점 : Request시 GET Method에 해당하는 정보만 전달가능하므로 정보전달 양이 한계가 있음. 그리고 미동기 통신이 안되는 문제점 있음.

그리고 장점 : 대용량 Response를 받을 수 있음. 사실상 브라우저 및 네트웍 성능에 달려 있음. 아무런 ActiveX나 Plugin이 필요치 않음. Cross-Browser 사실상 지원. JavaScript Tag가 먹히는지 여부 실제 확인 필요

(Concept)

1) Client에서 JSon Request URL 생성

2) Javascript createElement tag로 script 객체 생성

3) DOM에 해당 객체 추가하여 실제 반영. 객체추가시 즉각 Remote Server의 CGI/JSP/PHP 호출

4) Remote Server에서는 Request에 따른 업무 처리 후 결과를 callback method로 둘러싼 JSON Format으로 생성하여 화면 송출

5) Browser에서는 결과가 실제로는 script tag 내에 위치하면서 callback method를 호출한것과 동일한 효과발생.

6) callback method내에서 필요한 client측의 업무 처리.

JSON은 AJAX 통신은 아님 사실상 Dynamic Script Creation에 해당되며 script creation시 Remote Server의 script를 불러오는 형태임.

실제 업무에서 JSON과 AJAX를 적절히 섞어 쓸 필요 있음.

SAML을 이용한 SSO Service의 구현


이동산, Mountie Lee
mountie@paygate.net

1. 개요

SAML은 웹 브라우저에서의 SSO문제를 해결하기 위해서 OASIS의 연구결과로 탄생하였다.
인트라넷 레벨에서의 SSO는 다양한 방식들이 이용되어왔고 구현에 있어서도 크게 문제될요소는 적다.
예를 들어 Cookie 기반의 SSO, LDAP기반의 SSO, 인증서 기반의 SSO등이 있다.

그러나 도메인간의 SSO 구현을 위한 방식은 통제할 수 없는 외부 환경을 포함하므로 통일된 하나의 표준방식이 필요하게 되었고
SAML은 이러한 도메인간의 SSO구현을 가능하게하는 XML 표준이다.

사용되는 용어에 대해서 알아보자.
SAML : Security Asserting Markup Language, http://en.wikipedia.org/wiki/SAML 참조
SSO : Single Sign On 하나의 일관된 인증방식으로 여러 서비스에 로그온할 수 있는 방법
ACS : Assertion Consumer Service, Identity Provider에 의하여 인증된 사용자에 대한 정보가 담긴 SAML response정보를 verify 하고 서비스를 제공할 수 있도록 포워딩 한다

SAML에는 3가지 중요한 구성원이 존재한다.
  • Service Provider : 서비스를 제공하는 주체
  • 유저 : 서비스를 이용하는 사용자
  • Identify Provider : 유저에 대한 인증을 담당하는 주체

SAML의 특징은 Cross domain상황에서 표준화된 방식으로 SSO를 구현할 수 있으면서
Platform에 관계없이 다양한 환경에서 표준적인 방법으로 SSO 구현이 가능하다는 것이다.
실제 다양한 iPhone등 다양한 플랫폼에서 테스트해본 결과 잘 동작한다.


2. SAML Basic Steps

아래 그림은 Wikipedia에서 제시한 흐름도이다.

Google에서도 아래 그림을 제시하고 있다.

각 단계별 과정을 설명하면 아래와 같다.
  • 1단계 : 유저는 서비스 제공자(Service Provider)에게 접근한다. (서비스 이용을 위하여)
  • 2단계 : 서비스 제공자는 SAMLRequest를 생성한다. 생성된 SAMLRequest은 XML format의 텍스트로 구성된다.
  • 3단계 : 유저의 브라우저를 이용하여 인증 제공자(Identity Provider)로 Redirect 한다.
  • 4단계 : 인증 제공자(Identity Provider)는 SAMLRequest를 파싱하고 유저인증을 진행한다.
  • 5단계 : 인증제공자(Identity Provider)는 SAML Response를 생성한다.
  • 6단계 : 인증제공자(Identity Provider)는 유저 브라우저를 이용하여 SAMLResponse data를 ACS로 전달한다.
  • 7단계 : ACS는 Service Provider가 운영하게 되는데 SAMLResponse를 확인하고 실제 서비스 사이트로 유저를 Forwarding한다.
  • 8단계 : 유저는 로그인에 성공하고 서비스를 제공받는다.

위 단계중 SAMLResponse가 중요한 역할을 하는데 Identity Provider로서 SAMLResponse에 대하여 전자서명하고 ACS가 전자서명을 검증하여 유효한 Response인지를 확신할 수 있게 된다.

Identity Provider는 자체적인 다양한 방식으로 유저인증을 진행할 수 있으며 서비스 제공자는 Identity Provider를 신뢰하여 인증의 전권을 Identity Provider에 의존하게 되어 Identity Provider의 신뢰 및 책임부분이 중요한 요소이다.


3. SAML SSO 실제 구현

SAML SSO를 실제 구현해가면서 어떻게 동작하는지 살펴보자.

3.1 사전 준비

구현은 Service Provider로서의 단계와 Identity Provider로서의 단계를 모두 포함하여 Full Cycle을 순환할 수 있도록 하지만
실제로는 대부분 Service Provider나 Identity Provider의 역할중 어느 한 역할을 주로 하게 될것이다.

필요한 핵심 Library는 아래와 같다.

  • OpenSAML 2.0 Library
    • download from http://www.opensaml.org
    • OpenSAML 2.0은 JDK1.5에서 동작
  • xmldsig, xmlsec library,
    • from sun java web services development pack,
    • http://java.sun.com/webservices/downloads/previous/webservicespack.jsp
  • JDOM library
    • http://www.jdom.org
기타 apache commons의 유용한 Library들을 필요에 맞게 사용한다.

RSA Keypair를 준비한다.

openssl을 이용한 keypair 생성 command
$ openssl genrsa -out sso_private.pem 1024
$ openssl rsa -in sso_private.pem -pubout -ourform DER -out sso_public.der
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in sso_private.pem -out sso_private.der -nocrypt

생성된 2개의 파일은 sso_private.der, sso_public.der이다.

한가지 주의할점은 sso_public.der은 우리가 통상적으로 알고 있는 인증서(certificate)가 아니다.
certifificate는 인증기관으로부터 public key의 유효성을 인정받은 경우에 인증기관의 전자서명이 첨부되어 발행되는데
여기서는 original rsa public key만으로 SSO를 구현한다.

대신 신뢰성을 확보하기 위하여 identity provider의 public key는 신뢰성 있는 방식으로 service provider에게 전달되어야하며
이 전달과정이나 identity provider의 private key 관리에는 주의를 기울여야한다.

SAML에서 이용하는 xmldsig spec이나 구현 사례를 봐도 인증서를 탑재하여 SAML SSO를 분명히 진행할 수 있다.
이 경우에는 identity provider의 public key를 특별한 방법으로 전달할 필요 없이
service provider는 신뢰할 수 있는 CA(Certificate Authority)가 보장하는 전자인증서를 확보하고 인증서 CRL 또는 OCSP checking등을 추가할 수 있을것이다.

3.2 SAMLRequest 생성 및 redirect

이 단계에서는 유저가 서비스 제공자의 사이트에 접속하고 SAML SSO 로그인을 진행하기 위해 SAMLRequest 생성 및 redirect의 과정을 설명한다.

3.2.1 ServiceProviderForm : 실제 유저가 최초로 접근하는 페이지이다. 여기서부터 시작

<form name="ServiceProviderForm" action="https://api.paygate.net/t/sso/saml/CreateRequestServlet.jsp" method="post">

<input type="hidden" name="loginForm" value="https://api.paygate.net/t/sso/saml/login_form.jsp" />

<input type="hideen" name="providerName" value="paygate.net" />

<input type="hidden" name="RelayState" value="https://api.paygate.net/t/sso/saml/result_view.jsp" />

<input type="hidden" name="acsURI" value="https://api.paygate.net/t/sso/saml/ACS.jsp" />

<input type="submit" value="Sign On">

</form>

* loginForm : Identity Provider에 위치하는 인증을 위한 로그인 폼
* providerName : 서비스를 제공하는 Service Provider Name
* RelayState : ACS에서 인증을 마친후 최종적으로 Redirecting하게 되는 서비스 제공 페이지 URL
* acsURI : Identity Provider가 보낸 SAMLResponse를 검증(Verify)하고 실제 서비스 제공사이트로 Forwarding하게 되는 URL

3.2.2 CreateRequestServlet : SAMLRequst 데이터 생성 단계

  • 먼저 Parameter를 받고
String ssoURL = request.getParameter("loginForm");
String providerName = request.getParameter("providerName");
String RelayState = request.getParameter("RelayState");
String acsURI = request.getParameter("acsURI");

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String ssoURL = request.getParameter("loginForm");
String providerName = request.getParameter("providerName");
String RelayState = request.getParameter("RelayState");
String acsURI = request.getParameter("acsURI");
.....
}

  • SAMLRequest xml data를 생성
String SAMLRequest = createAuthnRequest(acsURI, providerName);
private String createAuthnRequest(String acsURL, String providerName)
throws SamlException {
String filepath = getServletContext().getRealPath("t/sso/saml/templates/" + SAML_REQUEST_TEMPLATE);
String authnRequest = Util.readFileContents(filepath);
authnRequest = StringUtils.replace(authnRequest, "##PROVIDER_NAME##", providerName);
authnRequest = StringUtils.replace(authnRequest, "##ACS_URL##", acsURL);
authnRequest = StringUtils.replace(authnRequest, "##AUTHN_ID##", Util.createID());
authnRequest = StringUtils.replace(authnRequest, "##ISSUE_INSTANT##", Util.getDateAndTime());
return authnRequest;
}

  • Identity Provider로 redirect할 URL생성
String redirectURL = computeURL(ssoURL, SAMLRequest, RelayState);
private String computeURL(String ssoURL, String authnRequest,
String RelayState) throws SamlException {
StringBuffer buf = new StringBuffer();
try {
buf.append(ssoURL);

buf.append("?SAMLRequest=");
buf.append(RequestUtil.encodeMessage(authnRequest));

buf.append("&RelayState=");
buf.append(URLEncoder.encode(RelayState));
return buf.toString();
} catch (UnsupportedEncodingException e) {
throw new SamlException(
"Error encoding SAML Request into URL: Check encoding scheme - "
+ e.getMessage());
} catch (IOException e) {
throw new SamlException(
"Error encoding SAML Request into URL: Check encoding scheme - "
+ e.getMessage());
}
}

  • CreateRequestServlet.java 구현 종합
/*
* CreateRequestServlet
*/
public class CreateRequestServlet extends HttpServlet {

private static final String SAML_REQUEST_TEMPLATE = "AuthnRequestTemplate.xml";

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String returnPage = "service_proc.jsp"; // 실제 forwarding수행할 JSP
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.setContentType("text/html; charset=UTF-8");

// get PARAMETERS
String ssoURL = request.getParameter("loginForm");
String providerName = request.getParameter("providerName");
String RelayState = request.getParameter("RelayState");
String acsURI = request.getParameter("acsURI");

String SAMLRequest;
String redirectURL;

try {
// create SAMLRequest
SAMLRequest = createAuthnRequest(acsURI, providerName);
request.setAttribute("authnRequest", SAMLRequest);

// compute URL to forward AuthnRequest to the Identity Provider
redirectURL = computeURL(ssoURL, SAMLRequest, RelayState);
request.setAttribute("redirectURL", redirectURL);

} catch (SamlException e) {
request.setAttribute("error", e.getMessage());
}

request.getRequestDispatcher(returnPage).include(request, response);
}
}

  • AuthnRequestTemplate.xml 참조
<?xml version="1.0" encoding="UTF-8"?>
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="##AUTHN_ID##"
Version="2.0"
IssueInstant="##ISSUE_INSTANT##"
ProtocolBinding="urn:oasis:names.tc:SAML:2.0:bindings:HTTP-Redirect"
ProviderName="##PROVIDER_NAME##"
AssertionConsumerServiceURL="##ACS_URL##"/>

3.2.3 service_proc.jsp : Identity Provider로 SAMLRequest를 forward

<%@page import="net.paygate.saml.util.RequestUtil"%>
<%@page import="java.net.*"%>
<%
String error = (String) request.getAttribute("error");
String authnRequest = (String) request.getAttribute("authnRequest");
String redirectURL = (String) request.getAttribute("redirectURL");
%>
<html><head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"><title>SAML-based Single Sign-On Service </title></head>
<%
if (error != null) {
%>
<body><center><font color="red"><b><%= error %></b></font></center><p>
<%
} else {
if (authnRequest != null && redirectURL != null) {
%>
<body onload="document.location = '<%=redirectURL%>';return true;">
<h1 style="margin-bottom:6px">Submitting login request to Identity provider</h1>
<%
} else {
%>
<body>
<center><font color="red"><b>no SAMLRequest or redirectURL</b></font></center><p>
<%
}
}
%>
</body></html>

3.3 SAMLRequest를 받고 유저인증 진행 단계

이 단계는 Identity Provider에서 진행하게 된다.
유저인증은 각 Identity Provider별로 다양한 방법을 취할 수 있고 여기서는 LDAP 인증을 사용하고 있다.

3.3.1 login_form.jsp : 기본적인 유저 인증 정보를 입력받는다.

  • Parameter를 받고
String SAMLRequest = request.getParameter("SAMLRequest");
String RelayState = request.getParameter("RelayState");

  • SAMLRequest를 Parsing한다.
String requestXmlString = ProcessResponseServlet.decodeAuthnRequestXML(SAMLRequest);
String[] samlRequestAttributes = ProcessResponseServlet.getRequestAttributes(requestXmlString);

  • 사용자 이름과 비밀번호를 입력받고
<input type="text" name="username" id="username" size="18">
<input type="password" name="password" id="password" size="18">

  • IdentityProviderForm을 생성한다.
<form name="IdentityProviderForm" action="....." method="post">
....
</form>

  • login_form.jsp 구현 종합
<%
String SAMLRequest = request.getParameter("SAMLRequest");
String RelayState = request.getParameter("RelayState");
String ServiceProvider = "";
if (SAMLRequest == null || SAMLRequest.equals("null")) {
ServiceProvider = "";
} else {
String requestXmlString = ProcessResponseServlet.decodeAuthnRequestXML(SAMLRequest);
String[] samlRequestAttributes = ProcessResponseServlet.getRequestAttributes(requestXmlString);
String issueInstant = samlRequestAttributes[0];
ServiceProvider = samlRequestAttributes[1];
String acsURL = samlRequestAttributes[2];
}
%>

<html><head><title>SSO Login Page</title></head>
<body>
<h1><%=ServiceProvider%> Service Login</h1>
<form name="IdentityProviderForm" action="https://api.paygate.net/t/sso/saml/ProcessResponseServlet.jsp" method="post">
<input type="hidden" name="SAMLRequest" value="<%=SAMLRequest%>"/>
<input type="hidden" name="RelayState" value="<%=RelayState%>"/>
<input type="hidden" name="returnPage" value="./login_proc.jsp">
username : <input type="text" name="username" id="username" size="18">
<br>
password : <input type="password" name="password" id="password" size="18">
<br>
<input type="submit" value="로그인">
</form>
</body></html>

3.4 유저 확인후 SAMLResponse 생성

3.4.1 ProcessResponseServlet

  • login_form.jsp에서 parameter를 받는다.
String SAMLRequest = request.getParameter("SAMLRequest");
String returnPage = request.getParameter("returnPage");
String username = request.getParameter("username");
String password = request.getParameter("password");
String RelayState = request.getParameter("RelayState");

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String SAMLRequest = request.getParameter("SAMLRequest");
String returnPage = request.getParameter("returnPage");
String username = request.getParameter("username");
String password = request.getParameter("password");
String RelayState = request.getParameter("RelayState");
...
}

  • SAMLRequest를 parsing한다.
String requestXmlString = decodeAuthnRequestXML(SAMLRequest);

public static String decodeAuthnRequestXML(String encodedRequestXmlString) throws SamlException {
try {
Base64 base64Decoder = new Base64();
byte[] xmlBytes = encodedRequestXmlString.getBytes("UTF-8");
byte[] base64DecodedByteArray = base64Decoder.decode(xmlBytes);
try {

Inflater inflater = new Inflater(true);
inflater.setInput(base64DecodedByteArray);
byte[] xmlMessageBytes = new byte[5000];
int resultLength = inflater.inflate(xmlMessageBytes);

if (!inflater.finished()) {
throw new RuntimeException("didn't allocate enough space to hold decompressed data");
}

inflater.end();
return new String(xmlMessageBytes, 0, resultLength, "UTF-8");

} catch (DataFormatException e) {

ByteArrayInputStream bais = new ByteArrayInputStream(base64DecodedByteArray);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InflaterInputStream iis = new InflaterInputStream(bais);
byte[] buf = new byte[1024];
int count = iis.read(buf);
while (count != -1) {
baos.write(buf, 0, count);
count = iis.read(buf);
}
iis.close();

return new String(baos.toByteArray());
}

} catch (UnsupportedEncodingException e) {
throw new SamlException("Error decoding AuthnRequest: Check decoding scheme - " + e.getMessage());
} catch (IOException e) {
throw new SamlException("Error decoding AuthnRequest: Check decoding scheme - " + e.getMessage());
}
}


  • SAMLRequest에서 Attribute을 발췌한다.
String[] samlRequestAttributes = getRequestAttributes(requestXmlString);
String issueInstant = samlRequestAttributes[0];
String providerName = samlRequestAttributes[1];
String acsURL = samlRequestAttributes[2];

public static String[] getRequestAttributes(String xmlString) throws SamlException {
Document doc = Util.createJdomDoc(xmlString);
if (doc != null) {
String[] samlRequestAttributes = new String[3];
samlRequestAttributes[0] = doc.getRootElement().getAttributeValue("IssueInstant");
samlRequestAttributes[1] = doc.getRootElement().getAttributeValue("ProviderName");
samlRequestAttributes[2] = doc.getRootElement().getAttributeValue("AssertionConsumerServiceURL");
return samlRequestAttributes;
} else {
throw new SamlException("Error parsing AuthnRequest XML: Null document");
}
}

  • User 인증을 진행한다.
boolean isValiduser = login(username, password);

private boolean login(String username, String password) {
LdapLoginHandler ldaplh = new LdapLoginHandler();

if (password.length() < 1) return false;
if (ldaplh.isValidOfficeUser(username, password)) {
return true;
} else {
return false;
}
}
* 실제 인증은 Identity Provider의 사정에 맞게 다양한 방식으로 진행한다.

  • RSA Keypair loading
String publicKeyFilePath = keysDIR + "paygate_public.der";
String privateKeyFilePath = keysDIR + "paygate_private.der";
RSAPrivateKey privateKey = (RSAPrivateKey) Util.getPrivateKey(privateKeyFilePath, "RSA");
RSAPublicKey publicKey = (RSAPublicKey) Util.getPublicKey(publicKeyFilePath, "RSA");

  • SAMLResponse의 유효기간정보 설정, 현시점부터 24시간 유효하게 설정함
long now = System.currentTimeMillis();
long nowafter = now + 1000*60*60*24;
long before = now - 1000*60*60*24;

SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
java.util.Date pTime = new java.util.Date(now);
String notBefore = dateFormat1.format(pTime);
java.util.Date aTime = new java.util.Date(nowafter);
String notOnOrAfter = dateFormat1.format(aTime);

request.setAttribute("notBefore", notBefore);
request.setAttribute("notOnOrAfter", notOnOrAfter);

  • 인증을 거친 로그인 유저네임과 유효기간을 포함한 전자서명전의 SAMLResponse XML data생성
String responseXmlString = createSamlResponse(username, notBefore, notOnOrAfter);

private String createSamlResponse(
String authenticatedUser,
String notBefore,
String notOnOrAfter) throws SamlException {
String filepath = getServletContext().getRealPath("t/sso/saml/templates/" + samlResponseTemplateFile);
String samlResponse = Util.readFileContents(filepath);
samlResponse = StringUtils.replace(samlResponse, "##USERNAME_STRING##", authenticatedUser);
samlResponse = StringUtils.replace(samlResponse, "##RESPONSE_ID##", Util.createID());
samlResponse = StringUtils.replace(samlResponse, "##ISSUE_INSTANT##", Util.getDateAndTime());
samlResponse = StringUtils.replace(samlResponse, "##AUTHN_INSTANT##", Util.getDateAndTime());
samlResponse = StringUtils.replace(samlResponse, "##NOT_BEFORE##", notBefore);
samlResponse = StringUtils.replace(samlResponse, "##NOT_ON_OR_AFTER##", notOnOrAfter);
samlResponse = StringUtils.replace(samlResponse, "##ASSERTION_ID##", Util.createID());
return samlResponse;
}
* createID()는 UniqueID를 생성하는 함수임.
* getDateAndTime()은 SAML date format에 맞게 날짜시간정보를 생성하는 함수임.
* SAML date format : yyyy-MM-ddThh:mm:ssZ (예: 2008-01-30T23:05:23Z)

  • SAMLResponse에 대하여 전자서명
String signedSamlResponse = SAMLSigner.signXML(responseXmlString, privateKey, publicKey);
* 이때 publicKey는 SAMLResponse message에 포함되지만 데이터 암호화에는 사용되지 않는다.
* xmldsig.jar, xmlsec.jar가 필요함

  • 서명된 SAMLResponse를 포함하여 ACS로 forwarding한다.
request.setAttribute("samlResponse", signedSamlResponse);
request.getRequestDispatcher(returnPage).include(request, response);

  • ProcessResponseServlet.java 구현 종합
/*
* ProcessResponseServlet
*/

public class ProcessResponseServlet extends HttpServlet {

private final String keysDIR = System.getProperty("PGV3_HOME")
+ SystemUtils.FILE_SEPARATOR + "SSO"
+ SystemUtils.FILE_SEPARATOR + "keys" + SystemUtils.FILE_SEPARATOR;

private final String samlResponseTemplateFile = "SamlResponseTemplate.xml";
private static final String domainName = "paygate.net";

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String SAMLRequest = request.getParameter("SAMLRequest");
String returnPage = request.getParameter("returnPage");
String username = request.getParameter("username");
String password = request.getParameter("password");
String RelayState = request.getParameter("RelayState");

boolean continueLogin = true;

if (SAMLRequest == null || SAMLRequest.equals("null")) {
continueLogin = false;
request.setAttribute("error","ERROR: Unspecified SAML parameters.");
request.setAttribute("authstatus","FAIL");

} else if (returnPage != null) {
try {

String requestXmlString = decodeAuthnRequestXML(SAMLRequest);
String[] samlRequestAttributes = getRequestAttributes(requestXmlString);
String issueInstant = samlRequestAttributes[0];
String providerName = samlRequestAttributes[1];
String acsURL = samlRequestAttributes[2];

boolean isValiduser = login(username, password); // 유저인증

if (!isValiduser) {
request.setAttribute("error", "Login Failed: Invalid user.");
request.setAttribute("authstatus","FAIL");
} else {
request.setAttribute("issueInstant", issueInstant);
request.setAttribute("providerName", providerName);
request.setAttribute("acsURL", acsURL);
request.setAttribute("domainName", domainName);
request.setAttribute("username", username);
request.setAttribute("RelayState", RelayState);

String publicKeyFilePath = keysDIR + "paygate_public.der";
String privateKeyFilePath = keysDIR + "paygate_private.der";
RSAPrivateKey privateKey = (RSAPrivateKey) Util.getPrivateKey(privateKeyFilePath, "RSA");
RSAPublicKey publicKey = (RSAPublicKey) Util.getPublicKey(publicKeyFilePath, "RSA");

long now = System.currentTimeMillis();
long nowafter = now + 1000*60*60*24;
long before = now - 1000*60*60*24;

SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
java.util.Date pTime = new java.util.Date(now);
String notBefore = dateFormat1.format(pTime);

java.util.Date aTime = new java.util.Date(nowafter);
String notOnOrAfter = dateFormat1.format(aTime);

request.setAttribute("notBefore", notBefore);
request.setAttribute("notOnOrAfter", notOnOrAfter);

if (!validSamlDateFormat(issueInstant)) {
continueLogin = false;
request.setAttribute("error", "ERROR: Invalid NotBefore date specified - " + notBefore);
request.setAttribute("authstatus","FAIL");
} else if (!validSamlDateFormat(notOnOrAfter)) {
continueLogin = false;
request.setAttribute("error", "ERROR: Invalid NotOnOrAfter date specified - " + notOnOrAfter);
request.setAttribute("authstatus","FAIL");
}

if (continueLogin) {
// 서명전의 SAMLResponse Message 생성
String responseXmlString = createSamlResponse(username, notBefore, notOnOrAfter);

// SAMLResponse에 대한 전자서명
String signedSamlResponse = SAMLSigner.signXML(responseXmlString, privateKey, publicKey);
request.setAttribute("samlResponse", signedSamlResponse);
request.setAttribute("authstatus","SUCCESS");
} else {
request.setAttribute("authstatus","FAIL");
}
}
} catch (SamlException e) {
request.setAttribute("error", e.getMessage());
request.setAttribute("authstatus","FAIL");
}
}
// Forward SAML response to ACS
response.setContentType("text/html; charset=UTF-8");
request.getRequestDispatcher(returnPage).include(request, response);
}
}

  • SamlResponseTemplate.xml 참조
<samlp:Response ID="##RESPONSE_ID##" IssueInstant="##ISSUE_INSTANT##" Version="2.0"
xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<Assertion ID="##ASSERTION_ID##"
IssueInstant="2003-04-17T00:46:02Z" Version="2.0"
xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
<Issuer>https://www.opensaml.org/IDP
</Issuer>
<Subject>
<NameID
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress">
##USERNAME_STRING##
</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/>
</Subject>
<Conditions NotBefore="##NOT_BEFORE##"
NotOnOrAfter="##NOT_ON_OR_AFTER##">
</Conditions>
<AuthnStatement AuthnInstant="##AUTHN_INSTANT##">
<AuthnContext>
<AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:Password
</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
</Assertion>
</samlp:Response>

3.5 SAMLResponse Message를 Service Provider의 ACS로 forward

3.5.1 login_proc.jsp : forwarding을 담당

<%@ page contentType="text/html; charset=UTF-8"%>
<%
String acsURL = (String) request.getAttribute("acsURL");
String samlResponse = (String) request.getAttribute("samlResponse");
String RelayState = (String) request.getAttribute("RelayState");
String authstatus = (String) request.getAttribute("authstatus");
if (authstatus == null) authstatus = "FAIL";
if (RelayState == null) RelayState = "";
%>
<html>
<head>
<title>forward to ACS</title>
</head>
<%
if (samlResponse != null && authstatus.equals("SUCCESS")) {
%>
<body onload="javascript:document.acsForm.submit();">
<form name="acsForm" action="<%=acsURL%>" method="post">
<div style="display: none">
<textarea rows=10 cols=80 name="SAMLResponse"><%=samlResponse%></textarea>
<textarea rows=10 cols=80 name="RelayState"><%=RelayState%></textarea>
</div>
</form>
<%
} else {
%><script>alert('Login error'); history.back(-2); </script><%
}
%>
</body>
</html>


3.6 Service Provider이 ACS에서 SAMLResponse를 verify

3.6.1 PublicACSServlet : Service Provider측의 ACS

  • Parameter를 받고
String SAMLResponse = request.getParameter("SAMLResponse");
String RelayState = request.getParameter("RelayState");

  • Identity Provider의 public Key를 load
RSAPublicKey publicKey;
publicKey = (RSAPublicKey) Util.getPublicKey(publicKeyFilePath,"RSA");

  • Identity Provider가 보내온 SAMLResponse를 서명검증(verify)
boolean isVerified = SAMLVerifier.verifyXML(SAMLResponse, publicKey);

  • 검증을 거친이후 실제 서비스 사이트로 forward 요청
request.getRequestDispatcher("./acs_proc.jsp").include(request, response);

  • PublicACSServlet.java 구현 종합
public class PublicACSServlet extends HttpServlet {
private final String keysDIR = System.getProperty("PGV3_HOME")
+ SystemUtils.FILE_SEPARATOR + "CryptoServer"
+ SystemUtils.FILE_SEPARATOR + "keys" + SystemUtils.FILE_SEPARATOR;

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String SAMLResponse = request.getParameter("SAMLResponse");
String RelayState = request.getParameter("RelayState");

// acs knows public key only.
String publicKeyFilePath = keysDIR + "paygate_public.der";

RSAPublicKey publicKey;
try {
publicKey = (RSAPublicKey) Util.getPublicKey(publicKeyFilePath,"RSA");

boolean isVerified = SAMLVerifier.verifyXML(SAMLResponse, publicKey);

if (isVerified) {

String loginid = null;
Document doc = Util.createJdomDoc(SAMLResponse);

Iterator itr = doc.getDescendants();

itr = doc.getDescendants(new ElementFilter());
while (itr.hasNext()) {
Content c = (Content) itr.next();
if (c instanceof Element) {
Element e = (Element) c;
System.out.println("Element:" + e.getName());

if ("NameID".equals(e.getName())) {
loginid = e.getText().trim();
break;
}
}
}
request.setAttribute("mid", loginid);
request.setAttribute("RelayState", RelayState);

response.setContentType("text/html; charset=UTF-8");
request.getRequestDispatcher("./acs_proc.jsp").include(request, response);
} else {
System.out.println("SAMLResponse is modified!!");
return;
}

} catch (SamlException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

3.7 완전하게 동작하는 WAR file

  • 화면상의 제약으로 인해 완전하게 동작하는 WAR File을 별도 준비하였다.
  • 파일이 필요한 분은 dev@paygate.net으로 메일요청하십시오.
  • 또한 아래 링크를 통해서 file download 위치를 확인할 수 있다.
    http://docs.google.com/Doc?id=dcxqxct2_4dsh8w4dt


4. SAML SSO 적용 실제 사이트

4.1 PayGate Admin Login

  • PayGate가 Service Provider 역할을 함.

4.1.1 최초 Service Login Page

  • 이 단계는 SAMLRequest를 생성하기전 서비스 제공자가 제시하는 화면이다.

4.1.2 Identity Provider로서 Login Form 제시

  • 이 화면이 보이기까지 서비스 제공자는 SAMLRequest를 생성하고
  • Identity Provider로 SAMLRequest를 redirect하면
  • 유저인증을 위해서 보여지는 화면이다.
  • 이 화면 이후 로그인 버튼을 누르게 되면 SAMLResponse 메세지를 생성하여 Service Provider의 ACS Site로 forwarding하게 된다.

4.1.3 Service Provider ACS verify를 거친후 마지막 페이지


  • Service Provider에 위치하는 ACS가 SAMLResponse를 verify한 이후 실제 서비스 제공 사이트로 forwarding한 결과이다.

4.2 Google Apps Service

  • google은 service provider역할을 하며
  • paygate는 identity provider역할을 한다.

4.2.1 gmail 서비스 로그인을 위한 인증 화면


  • google에서 SAMLRequest를 생성하여 identity provider인 페이게이트로 redirect하면
  • 페이게이트에서 유저 인증을 위하여 생성한 화면이다.

4.2.2 identity provider의 인증을 거친후 로그인된 화면


  • Identity Provider가 SAMLResponse를 생성하여
  • google acs로 forwarding하면 verify한 이후 실제 서비스 제공 사이트로 연결한다.


5. SAML의 확장

5.1 공인인증기관과 Identity Provider

SAML SSO는 국내에서는 공인인증기관이 Identity Provider역할을 한다면 아주 적당한 business model이 될것 같다.

공인인증기관의 신뢰성을 바탕으로 인증서 발행한 유저에 대한 확인을 직접 수행하고
Service Provider는 공인인증기관의 확인만을 검증하여 그 결과를 신뢰할 수 있다.

더 나아가서는 꼭 공인인증기관이 Identity Provider역할을 하지 않더라도
공인인증서 기반의 Public Identity Provider는 쉽게 예상할 수 있다.

5.2 Payment Gateway와 Identity Provider

결제대행사(Payment Gateway)사가 Identity Provider역할을 하고
쇼핑몰이 Service Provider가 되는 구조를 생각해볼 수 있다.

SAMLRequest는 충분히 확장가능한 구조이므로 Payment 요청에 필요한 필수정보 (상품명, 가격 등)을 포함하여
PG사에 대하여 Identity Provider로서 요청하고
PG사 사이트에서 안전하게 Payment를 처리하고 그 결과를 SAMLResponse format으로 돌려주는 구조이다.

이는 Cross Domain간에 Payment Protocol에 대한 표준이 부재한 현 상황에서
의미있게 시도해봄직한 목표이다.


6. 참고자료

SAML Single Sign On Service for Google Apps

  • http://code.google.com/apis/apps/sso/saml_reference_implementation.html

OpenSAML

  • http://www.opensaml.org/

SAML at Wikipedia

  • http://en.wikipedia.org/wiki/SAML