기술 포스트
신용카드 데이터 복호화 방법, 파트 II
고객들이 자주 묻는 질문: ID TECH 신용카드 리더기에서 출력되는 데이터를 어떻게 복호화할 수 있나요?
정답은 다음과 같습니다. 데이터 암호화에 사용된 알고리즘과 키를 알아야 합니다. 이 두 가지를 알면 해당 키를 사용하여 데이터를 복호화할 수 있습니다.
오늘날 거의 모든 신용카드 데이터는 DUKPT라는 특수 키 관리 방식을 통해 생성된 일회용 키로 암호화됩니다. DUKPT (Derived Unique Key Per Transaction의 약자)는 모든 거래에 고유한 키를 사용하는 방식입니다. 이 키는 다른 거래에 재사용할 수 없기 때문에, DUKPT 환경에서는 재전송 공격(replay attack)이 원천적으로 불가능합니다.
그렇다면 특정 거래를 복호화할 수 있는 DUKPT 키는 어떻게 도출할 수 있을까요? 일반적으로는 해당 거래의 키 일련번호(KSN)와 신용카드 리더기에 주입된 초기 키인 IPEK(Initial PIN Encryption Key)가 필요합니다. IPEK는 카드 리더기에 직접 주입되지 않는 최고 기밀 키인 BDK(Base Derivation Key)로부터 파생됩니다. BDK와 달리 IPEK는 특정 물리적 장치에 고유하게 부여됩니다. (하나의 BDK에서 여러 개의 고유한 IPEK를 생성할 수 있습니다.) 장치에 대한 IPEK를 모르는 경우(IPEK는 어디에도 기록되지 않으므로 알 수 없는 것이 당연합니다), KSN과 BDK를 사용하여 IPEK를 도출할 수 있습니다. 그 방법은 이 문서의 파트 I.
세션 키(워킹 키 또는 단순히 "데이터 키"라고도 함)를 도출하는 과정은 크게 3단계로 구성됩니다. 각 단계는 다음과 같습니다.
1. BDK와 KSN을 사용하여 IPEK를 도출합니다. ( 이 문서의 파트 I 이 작업을 수행하는 방법에 대한 자세한 내용은 해당 항목을 참조하십시오.)
2. ANSI X9.24 (DUKPT) 키 파생 알고리즘을 사용하여 KSN과 IPEK로부터 기본 키(초기 "파생 키")를 도출합니다.
3. 2단계에서 도출한 파생 키를 Data Key, PIN Key, 또는 MAC Key 중 원하는 형태로 변환합니다. (대부분의 신용카드 리더기는 트랜잭션 세션 키로 Data 변형 키를 사용하도록 설정되어 있지만, 일부는 PIN 변형 키를 사용하도록 설정되어 있는 경우도 있습니다.)
이제 3단계 프로세스 중 가장 까다로운 부분인 "파생 키" 도출(2단계)에 대해 살펴보겠습니다. 파생 키를 확보한 후에는 이를 Data, PIN, 또는 MAC 변형으로 변환하는 방법을 설명할 예정이며, 이 단계는 비교적 간단합니다.
아래에서 상당량의 의사 코드(pseudocode)를 사용할 예정이지만, 다음에 설명하는 모든 단계에 대한 완전한 동작 가능한 소스 코드(JavaScript)는 인기 있는 저희 암호화/복호화 도구에서 확인하실 수 있습니다. (아직 시도해보지 않으셨다면 지금 바로 사용해 보세요. 최신 브라우저에서 동작하는 독립형 웹 페이지입니다.)
키 파생
Data, PIN, 또는 MAC 변형을 생성할 수 있는 기본 키를 도출하려면 트랜잭션 KSN과 IPEK가 필요합니다. 해당 값을 확보한 후(다시 한번 말씀드리지만: 이 시리즈의 1부참조), 다음 단계를 수행하십시오.
1. 10바이트 KSN의 하위(가장 오른쪽) 8바이트를 추출합니다. 상위 2바이트는 버립니다.
2. 8바이트 KSN의 마스킹된 버전을 저장할 BaseKSN 변수를 생성합니다. 1단계에서 구한 8바이트 KSN과 (16진수) 값 0xFFFFFFFFFFE00000을 AND 연산하여 마스킹된 버전을 구합니다.
3. 원본(마스킹되지 않은!) 10바이트 KSN의 하위 3바이트와 0x1FFFFF를 AND 연산하여 카운터 비트를 구합니다. (KSN의 하위 21비트가 트랜잭션 카운터를 구성한다는 점을 상기하십시오.) 이 값은 counter.
4. 16바이트 IPEK를 curKey.
5. 이제 루프를 설정해야 합니다. 루프를 반복할 때마다 counter 비트를 검사합니다(최상위 비트인 21번째 비트부터 시작하여, 두 번째 반복에서는 20번째 비트, 그다음에는 19번째 비트 순으로 진행합니다). 켜져 있는 비트를 발견할 때마다 해당 비트를 BaseKSN에 OR 연산으로 적용한 후, generateKey() 를 호출하여 curKey를 업데이트합니다. BaseKSN은 루프를 반복할 때마다 비트를 누적하며, curKey 값은 켜져 있는 counter 비트를 발견할 때마다 업데이트됩니다.
그렇다면 generateKey() 는 어떤 역할을 할까요? 좋은 질문입니다! 사용하는 프로그래밍 언어가 BigInteger 연산을 지원하는 경우, 코드는 다음과 같은 형태가 됩니다.
네, 확인하실 수 있듯이, 16바이트 키에 마스킹을 적용한 후 이를 사용하여 8바이트 ksn 값을 암호화함으로써 새로운 키의 왼쪽 절반(왼쪽 8바이트)을 생성합니다. 새로운 키의 오른쪽 절반은 동일한 ksn을 사용하되, 마스킹이 적용되지 않은 키로 생성한 암호문입니다.
마지막으로, encryptRegister() 함수가 어떻게 생겼는지 알아야 합니다. 다음과 같습니다:
여기서 Cipher Block Chaining은 사실상 의미가 없습니다. 8바이트 값(데이터 블록 하나)을 암호화하는 것이기 때문에 "체이닝"할 대상이 없기 때문입니다. 이 코드에 포함된 이유는 단순히 암호화 루틴이 체이닝 여부를 지정하는 매개변수를 요구하기 때문입니다.
또한 암호화에 8바이트 키를 사용하고 있다는 점에 유의하십시오. TDES는 키가 8바이트인 경우 단일 DES로 동작합니다. 이는 8바이트 키를 triple DES에 적용하면 암호화/복호화/암호화 사이클이 단일 암호화와 동일한 결과를 내기 때문입니다.
이 루틴의 동작을 쉽게 설명하면, 16바이트 키의 상위 8바이트를 사용하여 키의 하위 8바이트와 (8바이트) KSN을 XOR한 특수 값을 암호화합니다. 그 결과는 KSN의 단방향 해시값이 됩니다.
이 모든 과정을 종합하면, 위의 5단계 루프는 curKey 값을 생성하며, 이 값은 Data, PIN, 또는 MAC 파생 키를 도출하기 위한 기반 키가 됩니다. (5단계의 루프는 최종적으로 기반 키인 curKey를 반환하는 함수의 일부이거나 그렇게 구성되어야 합니다.)
이제 세 가지 "키 변형(key variant)" 옵션을 더 자세히 살펴볼 차례입니다.
Data, PIN, MAC 키 변형 생성
ANSI X9.24에서는 DUKPT 키가 변형(variant)이라 불리는 세 가지 최종 형태 중 하나를 취할 수 있도록 허용합니다. 각 형태는 MAC, PIN, Data입니다. 이 키 유형들의 용도에 대한 설명은 뒤로 미루고, 생성 방법에 집중하겠습니다.
모든 변형의 출발점은 DUKPT 기반 키(파생 키, 즉 앞서 curKey 위의 5단계에서). MAC 변형 키를 얻으려면, 기본 키("파생 키")를 특수 상수와 XOR 연산하기만 하면 됩니다:
PIN 변형 키도 마찬가지로 유사한 방식으로 생성되지만, 다른 상수를 사용합니다:
Data 변형 키에는 또 다른 상수가 필요합니다:
MAC 및 PIN 변형 키의 경우, XOR 연산이 해당 세션 키 생성의 최종 단계입니다. Data 변형 키의 경우에는 관례적으로 단방향 해시를 포함하는 추가 단계를 수행합니다(Data 키를 MAC 키로 역변환할 가능성을 원천 차단하기 위함입니다). 의사 코드(pseudocode)로 나타내면 다음과 같습니다:
설명하자면 다음과 같습니다. 먼저 EDE3 확장 방식을 사용하여 파생 키의 24바이트 버전을 생성합니다. (이는 16바이트 키의 앞 8바이트를 키의 끝에 복사하여, 처음과 마지막 8바이트가 동일한 24바이트 키를 만드는 방식입니다.) 해당 키를 사용하여 16바이트 파생 키의 앞 8바이트를 TDES 암호화함으로써 8바이트 암호문을 생성합니다. 이것이 최종 데이터 키의 좌측 절반입니다. 우측 절반을 생성하려면, 동일한 24바이트 키를 사용하여 파생 키의 뒤쪽 8바이트를 암호화합니다. 두 개의 8바이트 암호문(좌측과 우측 부분)을 결합하면 완성됩니다.
검증용 기준값
직접 구현을 시도하는 경우, 검증용 기준값과 대조하여 결과를 확인하는 것이 좋습니다. 먼저 16바이트 BDK로 0123456789ABCDEFFEDCBA9876543210(16진수)을 사용하십시오. 이는 일반적으로 널리 사용되는 테스트 키 값입니다. 테스트 KSN 값으로는 629949012C0000000003을 사용해 보십시오. 이 두 값을 통해 IPEK D2943CCF80F42E88E23C12D1162FD547을 도출할 수 있어야 합니다. (IPEK 도출 방법은 이 문서의 파트 I 을 참고하십시오.)
위에서 언급한 IPEK를 시작점으로 하여 "파생 키"(또는 DUKPT 기본 키)를 도출할 때, 다음과 같은 값들이 나타나야 합니다:
KSN 카운터 루프의 "if" 조건을 처음 통과할 때, BaseKSN 값은 49012C0000000002가 되며, curKey 은(는) 다음 처리 후 B58CDA5C7A1E9FF5E7335B988626D01A가 됩니다. generateKey().
카운터 루프의 "if" 구문을 두 번째로 통과하면, 카운터의 "ON" 비트 두 개를 모두 처리한 상태가 되며, 이때 BaseKSN은 49012C0000000003이 되고 결과로 도출된 curKey는 841AB7B94ED086EBC2B8A8385DA7DFCA가 됩니다. (카운터 비트를 MSB부터 순서대로 BaseKSN에 OR 연산으로 적용하게 됩니다. 카운터가 0x0F로 끝나는 경우, BaseKSN은 49012C0000000008에서 49012C000000000C, 49012C000000000E, 49012C000000000F 순으로 변경됩니다.)
따라서 "파생 키"는 841AB7B94ED086EBC2B8A8385DA7DFCA가 됩니다.
데이터 변형 상수와 XOR 연산을 수행하면, 파생 키는 841AB7B94E2F86EBC2B8A8385D58DFCA로 변경됩니다.
EDE3 확장 키 841AB7B94E2F86EBC2B8A8385D58DFCA841AB7B94E2F86EB를 사용하여 해당 값의 상위 절반과 하위 절반을 각각 암호화하면, 최종 데이터 키로 F739AEF595D3877F731782D28BB6AC4F를 얻게 됩니다. 즉, 24바이트 EDE3 키를 사용하여 841AB7B94E2F86EB를 암호화하면 암호문 F739AEF595D3877F가 생성되고, 동일한 키로 C2B8A8385D58DFCA를 암호화하면 암호문 731782D28BB6AC4F가 생성됩니다. 두 암호문을 연결하면 작업이 완료됩니다. 이제 KSN이 629949012C0000000003인 거래의 데이터를 복호화할 수 있는 16바이트 키를 확보하게 됩니다.
예제 코드: ID TECH 암호화/복호화 도구
여기서 설명한 모든 DUKPT 키 파생 루틴의 전체 소스 코드를 확인하려면, HTML 및 JavaScript 기반으로 제작된 암호화/복호화 도구을 다운로드하여 소스 코드를 직접 살펴보시기 바랍니다. 이 도구는 IPEK 계산, 세 가지 DUKPT 키 변형 파생, TDES 또는 AES를 이용한 데이터 암호화 및 복호화 등 다양한 기능을 지원합니다. Chrome의 강력한 개발자 콘솔 도구를 활용하면 실시간으로 코드를 단계별로 실행하고, 변수 값 변화를 확인하며, 중단점을 설정하는 것도 가능합니다. 학습에 매우 유용한 도구이며, 무료로 제공됩니다. 지금 바로 암호화/복호화 도구을 다운로드하여 직접 사용해 보시고, 주변에도 알려주세요. 제가 아는 한, 웹에서 제공되는 순수 JavaScript 기반 DUKPT 구현 도구로는 유일한 사례입니다.
