비대칭키를 이용한 암호/서명
- 비대칭키: 개인키 및 공개키로 나누어 활용
- 공개키: 암호화 작업만 할 수 있음. 누구에게나 공개되어도 상관 없음
- 개인키: 복호화 작업을 위해 사용. 공개되면 안되는 키
디지털 서명
- '원래 자료'를 '해쉬'로 만든 다음,
- ‘개인 키'로 해당 해쉬 값을 ’암호화' 하고 (서명),
- 다른 사람들은 '원래 자료'를 '해쉬'로 만든 다음,
- 서명 값을 '공개 키'로 '복호화' 해서 비교하여
- 두 해쉬 값을 비교하여 일치하면 서명이 유효함.
즉 인증서를 통한 로그인시
- 사용자: 인증서의 고유 번호 + challenge 받은 메시지에 대한 서명 값을 server로 전송
- 서버: 해당 고유 번호에 등록된 공개키로 이 사용자가 서명한 값이 맞는지 확인 (검증은 공개키로 가능. 검증에 성공했다는 것은 이 사용자가 개인키를 가지고 있다는 이야기)
(실제 서버단에서는 어떤 다른 추가적인 방법이 있는지는 알 수 없으나 아무튼 정해서
공인인증서 관련 기술들
- http://www.rootca.or.kr/kor/standard/standard01B.jsp 를 참고
- 개인키: signPri.key 에 암호화 되어 들어있다.
- 공개키: signCert.der 에 들어있다.
- 인증서 발급처 마다 조금씩은 다르지만, 은행에서 발급 받은 yessign 공인인증서를 이용하여 테스트
공개키 파일
- signCert.der
- asn1 형식
- 안에 다시 asn1 형식
- 해당 파일이 PKCS#1인데 pub key 만 있었음
개인키 파일
- signPri.key
- PKCS#5 의 PBKDF1방식을 이용하여 복호화 한다. (키 파일에서 salt, integration count 를 구하고, 비밀번호는 공인인증서 비밀번호)
위 두 파일에 대한 처리 방법은 아래 링크를 참고
- https://gist.github.com/twkang/f5acf360c67ea0bf3f55
테스트해봅시다.
(파이선 코드)
원본(3): “abc”
sha256(64H/32B) ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
EME-PKCS1-v1_5 padding 처리 (msg = ‘abc’, key 길이 = 256, hash=sha256)
0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
(앞에 \x00\x01 붙이고, \xff로 쭉 채우고 \x00 붙이고 해쉬 종류에 따른 prefix 붙이고 abc에 대한 해쉬값을 붙인다.)
이 값을 개인키를 이용하여 ‘복호화’ 한다.
(암호화도 안했는데 왜 복호화부터 하냐고 묻지말자. 그냥 이렇게 정한것이다. 정말 PKI기반 디지털 서의 이론적인 원리가 궁금하다면 http://en.wikipedia.org/wiki/Digital_signature#How_they_work 를 참고하자.)
-> (256바이트/512Hex 데이터)
이 값이 서명값이고, 실제 플러그인에서 만들어 낸 서명문의 서명값과 일치한다.
(실제 플러그인 구동)
원본: SignData(‘abc’,’’,0)
결과값(3760H):
-> 3082...
여기서 뽑아낸 hash (512H/256B)
-> 26...76
이 hash에 대한 pubkey의 encrypt 결과 (510H/255B)
-> 01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
서명(sign) : 복호화(decrypt)
검증(verify) == 암호화 후 메시지 같은지 비교
<실험>
서명 데이터를 다르게 했을 때 두 값이 달랐는데, 하나는 pkcs7-data(이게 아마 format에 맞춘게 아닐까), 나머지 하나가 해쉬.. 근데 그러면 pkcs7-data를 sign해야되나...
#1 mitmproxy 를 이용하여 트래픽 분석
#2 트래픽 일부 분석
#3 인증서 구조 분석
#4 서명 결과 분석
- ***** 사이트에서 로그인 시도
- yessignCrypto.js 스크립트에서 서명
- inputStr 에 서명할 데이터 포함 (timestamp 포함한 form data)
- yessignCrypto.SignData(inputStr, ssn, option) 을 통해 서명 (ssn, option에는 blank string)
- 공인인증서 로그인 창이 뜬 다음 서명 데이터 나온다 (여러번 요청해도 똑같은 데이터가 나옴 - 즉 서명 단계에서는 더이상 랜덤 값을 넣지 않는다)
- 이 결과(A)에 몇 가지 form data 를 추가하여 POST 요청
#5 결과(A)에 대한 분석
- 길이는 3966 바이트 (어느정도 변화 가능 - hex encoded 데이터)
- hex decode 결과는 약 1983 바이트 결과(A-1)
- 결과(A-1)은 30 82 로 시작하는 0x3082 == ASN.1 Sequence (http://etherhack.co.uk/asymmetric/docs/rsa_key_breakdown.html)
- 이 결과(A-1)를 파일로 저장하여 openssl 로 열어봅시다
- $ openssl asn1parse -in FILENAME -inform der
- pkcs7-data 로 174바이트의 hex encoded 데이터 (hex decode 후 euc-kr 로 decode시 원본 서명 텍스트 나옴)
- rsaEncryption 로 512바이트의 hex encoded 데이터 (hex decode 후 256바이트의 raw data)
- sha256WithRSAEncryption 로 2048비트(256바이트)의 이진수 데이터 (raw data). RSA암호 후 이걸로 해쉬하면 이게 나오나
- 특별히 이름은 없는데 270바이트짜리 2진수 raw data도 존재. 서명 단계에서 원본값을 변조한 결과 변경되지 않으므로 무시.
- 즉 sha256WithRSAEncryption 값이 서명 데이터임을 알 수 있음
#6 서명데이터(256바이트)에 대한 분석
- object tag를 통해 active x 를 호출하는 과정에서, 같은 인증서를 이용한 경우 해쉬 값에는 오로지 메시지만 영향을 주는 것을 확인
-> 즉 메시지에 대한 서명값
#* 기타
- 전자 서명문은 PKCS#7 을 이용