본문 바로가기
Quality control (Univ. Study)/Information Security

Cryptography Lab (2)

by 생각하는 이상훈 2024. 12. 17.
728x90

X.509 인증서 검증

이 작업에서는 프로그램을 사용해 X.509 인증서를 수동으로 검증할 것이다. X.509 인증서는 공개 키에 대한 데이터와 그 데이터에 대한 발급자의 서명을 포함한다. 실제 X.509 인증서를 웹 서버에서 다운로드한 뒤, 발급자의 공개 키를 가져와 이 공개 키로 인증서의 서명을 검증한다.

Step 1: 실제 웹 서버에서 인증서 다운로드 받기. 아래 명령어를 사용하거나, 웹 브라우저를 통해서 인증서를 다운받을 수 있다.

우선 예시 코드를 따라서 실행해보자.

이건 예시 코드이니 이번에는 인하대학교의 인증서를 다운받아보자.

 


공개키 (e, n)을 issuer 의 인증서로부터 추출하기

OpenSSL 을 이용하면 x509 인증서의 특정 값들을 추출할 수 있다. 예를 들면 n 값은 -modulus 를 이용해서 추출할 수 있다. 공개키 e 값을 추출하는 커멘드는 따로 없지만, 전체 필드를 출력하여 e 값을 쉽게 찾을 수 있다.

signature파일을 만들어둔다.

(base) (base) vcl@vcl:/media/vcl/InhaData/sanghoon/cryptography$ openssl x509 -in c1.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            09:0e:e8:c5:de:5b:fa:62:d2:ae:2f:f7:09:7c:48:57
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root G2
        Validity
            Not Before: Nov  2 12:24:25 2017 GMT
            Not After : Nov  2 12:24:25 2027 GMT
        Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=Thawte TLS RSA CA G1
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c6:39:e0:98:f8:55:7a:d0:b4:6f:fa:33:6d:82:
                    5d:cc:e0:54:03:5b:0c:a2:0e:3b:d3:7d:1c:00:ff:
                    8f:db:70:0d:50:df:20:ad:71:02:2f:c3:61:0c:41:
                    78:17:54:7d:b4:bd:30:63:49:9c:cc:76:91:d1:ae:
                    e5:61:a9:e5:c6:dc:16:a3:5b:36:b8:69:e7:c8:3b:
                    3a:98:e0:ac:eb:a7:b0:db:0d:d8:11:3a:fa:4d:bd:
                    78:c6:08:e9:bb:58:06:16:d0:1e:7b:06:a2:90:ef:
                    45:b9:df:21:c4:62:53:4b:09:fc:c5:e3:64:7c:a5:
                    56:a4:3d:8b:e2:f1:4d:df:a1:4d:83:17:a2:94:ae:
                    9a:13:8c:a4:80:60:33:36:5a:24:4e:9e:a1:34:e2:
                    c0:62:90:f2:49:d2:c0:3c:ac:ee:25:24:3b:24:21:
                    19:e8:ef:92:0c:ac:b0:21:d5:cb:a0:c4:e7:a7:1b:
                    81:28:64:86:f3:c3:56:4e:8d:c2:1c:23:86:99:01:
                    02:89:ad:b2:a9:d3:c3:8e:02:ea:9c:48:98:36:3c:
                    10:2f:cb:8c:aa:3f:2b:3a:f9:4c:82:f8:81:70:70:
                    3b:c6:dc:be:ef:fb:98:2c:de:99:4b:b5:6a:d7:f1:
                    7f:95:58:55:39:fe:5e:8f:a8:d9:76:60:7c:e6:cc:
                    c5:6d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                A5:8C:FE:32:CC:EB:0F:2C:D4:19:C6:08:B8:00:24:88:5D:C3:C5:B7
            X509v3 Authority Key Identifier: 
                4E:22:54:20:18:95:E6:E3:6E:E6:0F:FA:FA:B9:12:ED:06:17:8F:39
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            Authority Information Access: 
                OCSP - URI:http://ocsp.digicert.com
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://crl3.digicert.com/DigiCertGlobalRootG2.crl
            X509v3 Certificate Policies: 
                Policy: X509v3 Any Policy
                  CPS: https://www.digicert.com/CPS
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        ba:92:6d:0a:03:8b:13:6f:65:58:a4:40:66:fe:e2:f6:1c:bf:
        e9:65:7f:41:ec:bf:e1:6c:9e:0d:72:80:5e:ed:5e:7a:a0:29:
        ed:ed:a7:88:a3:cb:0c:8c:24:56:4c:25:99:0f:57:58:d3:ed:
        8a:64:e0:b5:74:a8:fc:77:55:57:5c:0b:67:8f:2b:43:0e:e3:
        cf:7f:af:e2:a3:0d:26:61:04:ce:fc:60:20:fc:c2:f2:2f:a0:
        83:9b:71:73:0c:1f:15:b6:c1:ff:69:e3:20:3f:aa:60:0f:55:
        d0:ab:3f:a1:68:39:df:9c:94:ca:06:ec:61:72:99:f1:dc:07:
        5b:95:eb:9e:fd:09:cf:7f:58:47:61:af:0b:f9:1b:fc:3e:2e:
        54:87:85:7d:17:01:ce:7e:98:5d:31:73:b1:8b:5e:0e:aa:6b:
        22:4d:b7:39:70:eb:3d:fe:eb:a4:1f:e6:15:b2:e1:5d:59:39:
        da:e8:85:70:d6:a8:7e:b4:4b:72:1f:5e:91:be:68:bb:a6:4a:
        b2:65:85:0b:38:f3:08:13:b6:af:ae:58:d5:54:16:6e:8a:4c:
        00:46:d6:3c:b4:25:55:e8:fa:7d:97:75:5e:6a:00:6a:6f:67:
        df:82:a3:49:b7:70:b4:4d:83:58:40:8f:81:5c:6d:51:d5:c0:
        01:96:89:5d

서버의 인증서에서 서명을 추출하기

OpenSSL에는 서명 필드를 직접 추출하는 명령어는 없다. 하지만 인증서의 모든 필드를 출력한 뒤, 서명 블록을 복사해서 파일에 저장할 수 있다.

(base) (base) vcl@vcl:/media/vcl/InhaData/sanghoon/cryptography$ openssl x509 -in c0.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            08:5a:68:42:6d:ad:eb:17:50:b4:4c:d7:3e:58:91:0a
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=Thawte TLS RSA CA G1
        Validity
            Not Before: Sep  4 00:00:00 2024 GMT
            Not After : Oct  3 23:59:59 2025 GMT
        Subject: C=KR, ST=Incheon, L=Michuhol-gu, O=Inha University, CN=*.inha.ac.kr
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:dd:12:f6:81:91:39:89:c0:49:fe:7b:58:fd:f4:
                    c8:c9:d2:69:0d:68:f8:de:de:d1:52:a2:2a:bb:df:
                    25:c2:f3:b4:06:3a:cc:24:03:ba:13:a1:7d:66:da:
                    c1:e6:c1:18:64:7d:d2:03:e0:fa:d0:95:55:55:60:
                    c6:a0:6f:a5:78:74:f5:a1:8c:fd:84:3a:5a:ac:30:
                    56:6a:8f:a1:43:be:eb:98:fe:6b:70:65:8b:61:8d:
                    91:25:c4:6f:6f:e7:73:b3:a7:a2:41:d7:ce:ca:e2:
                    a0:67:e3:7c:12:fd:1f:45:23:18:c4:77:bd:2d:4e:
                    11:6d:e5:a1:9e:02:20:a6:e9:25:ef:82:8f:94:7e:
                    56:ec:5e:88:79:ca:c3:45:4b:a7:d7:4c:ff:9d:68:
                    41:87:eb:d1:68:b8:e7:49:a5:c9:fb:10:87:7c:07:
                    bb:db:5e:ff:99:21:4e:72:d9:08:6f:f8:78:09:90:
                    86:19:14:3c:f9:cb:33:4e:d8:9c:52:fe:c5:0a:6e:
                    08:94:1f:16:53:38:dd:5f:09:09:8b:12:d7:38:34:
                    9b:06:4b:e1:65:20:5e:74:db:ce:82:7e:63:8f:63:
                    89:60:c5:fc:af:b1:92:02:06:70:a7:31:7a:4b:ea:
                    d8:dd:8b:d9:e8:d6:8a:c4:1d:81:b7:fd:03:e7:73:
                    43:15
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                A5:8C:FE:32:CC:EB:0F:2C:D4:19:C6:08:B8:00:24:88:5D:C3:C5:B7
            X509v3 Subject Key Identifier: 
                73:DB:68:4D:5E:47:32:BE:16:AE:A6:9C:55:FE:86:B1:BC:01:E1:2D
            X509v3 Subject Alternative Name: 
                DNS:*.inha.ac.kr, DNS:inha.ac.kr
            X509v3 Certificate Policies: 
                Policy: 2.23.140.1.2.2
                  CPS: http://www.digicert.com/CPS
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://cdp.thawte.com/ThawteTLSRSACAG1.crl
            Authority Information Access: 
                OCSP - URI:http://status.thawte.com
                CA Issuers - URI:http://cacerts.thawte.com/ThawteTLSRSACAG1.crt
            X509v3 Basic Constraints: critical
                CA:FALSE
            CT Precertificate SCTs: 
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 12:F1:4E:34:BD:53:72:4C:84:06:19:C3:8F:3F:7A:13:
                                F8:E7:B5:62:87:88:9C:6D:30:05:84:EB:E5:86:26:3A
                    Timestamp : Sep  4 06:31:05.376 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:44:02:20:4F:5E:C0:9A:A8:04:54:88:3A:AD:78:80:
                                2F:6A:A8:1F:2C:C6:77:AB:00:FF:7A:A7:AD:EE:44:4A:
                                09:28:92:5F:02:20:3C:9D:15:89:32:DB:24:47:9E:6A:
                                1C:0A:CE:A3:11:77:53:73:E0:DB:C2:61:BC:3A:AC:FB:
                                53:50:04:8D:34:9D
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 7D:59:1E:12:E1:78:2A:7B:1C:61:67:7C:5E:FD:F8:D0:
                                87:5C:14:A0:4E:95:9E:B9:03:2F:D9:0E:8C:2E:79:B8
                    Timestamp : Sep  4 06:31:05.264 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:46:02:21:00:82:FF:E1:89:33:8D:97:4F:20:D4:C1:
                                53:84:C3:AE:4C:CE:EF:9C:9E:A7:FB:B3:AB:55:C0:20:
                                4D:76:C6:D8:47:02:21:00:AC:41:3A:69:09:A3:9B:01:
                                A7:6C:71:5E:B9:D0:41:30:21:72:98:A5:57:B5:8F:FA:
                                A7:BD:95:BC:66:04:EF:DC
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : E6:D2:31:63:40:77:8C:C1:10:41:06:D7:71:B9:CE:C1:
                                D2:40:F6:96:84:86:FB:BA:87:32:1D:FD:1E:37:8E:50
                    Timestamp : Sep  4 06:31:05.279 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:D6:EA:3F:AC:B5:D5:0B:63:BD:F2:DC:
                                63:10:61:15:0C:44:E3:15:F2:7A:D4:1A:89:11:7F:9F:
                                C5:C5:79:2D:4B:02:20:04:89:B4:BB:F1:38:2E:62:F4:
                                E5:F1:06:E3:88:54:BD:29:A7:05:C9:6E:E6:0E:3D:65:
                                D9:7F:6F:FF:13:9D:E0
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        87:af:a2:b4:dd:35:31:6d:0c:f3:62:0f:14:e8:87:e8:cd:a1:
        ee:27:d6:9e:f6:01:44:9b:bd:b6:b9:57:ec:08:95:fd:4a:51:
        5a:bc:ce:3e:d6:63:57:0c:ae:ad:92:1d:79:82:f2:0f:07:7a:
        c4:df:5d:ee:44:8f:7a:31:85:67:e8:24:29:c4:08:23:a8:37:
        eb:bc:c4:89:34:e9:aa:52:1b:77:76:20:01:1d:4a:d2:11:56:
        5e:e3:08:7d:82:12:80:b8:5c:b2:0e:45:32:66:3e:48:ba:9d:
        74:d0:b3:37:a9:6f:70:c3:c7:22:b5:a0:7c:f0:bd:c1:80:fb:
        42:1e:6e:58:fe:67:4b:49:44:bb:28:fb:4a:59:28:64:f4:22:
        42:33:0a:5d:ad:5c:54:79:ee:e8:b0:6c:92:d1:e5:ae:eb:67:
        73:b1:97:ec:e2:75:93:93:56:23:61:b7:09:46:7d:9c:b5:c9:
        84:8c:7f:5e:6f:13:5c:8b:79:48:cb:9e:3b:db:65:13:e0:e9:
        33:3a:7a:65:49:5d:ce:88:c4:16:b4:be:51:eb:f9:49:1d:d3:
        00:fc:f5:b2:f4:1e:3b:c3:a0:24:09:1c:ed:82:e9:03:0d:b3:
        b5:a8:49:a1:3d:4e:e3:ac:cc:3b:d9:1e:30:3e:1d:c4:33:eb:
        6d:9b:13:0c

 

필요없는건 날려서 cleaned_signature에 따로 저장을 했다.


서버의 인증서의 본문 추출하기

인증 기관(Certificate Authority, CA)은 서인증서의 서명을 생성할 때 먼저 인증서의 해시 산한 , 그 해시를 서명한다. 서명을 검증하면 인증서로부동일한 해시 을 생성해야 한다. 그러나 해시는 서명이 산되기 전에 생성되로, 인증서에서 서명 블록을 제하고 해시를 산해야 한다. 인증서의 어이 해시 생성에 사용되는지 알아내는 것은 인증서 포에 대한 깊은 이해 없이는 운 작업이다.

X.509 인증서는 ASN.1(Abstract Syntax Notation One) 표준을 사용해 인코된다. ASN.1 구파싱할 수 있다면 인증서의 정 필드를 쉽게 추출할 수 있다. OpenSSL 은 asn1parse 명어를 제공하며, 이를 사용해 ASN.1 형식으로 인코추출할 수 있고, X.509 인증서를 파싱하는 에도 사용할 수 있다.

X.509 인증서에서 시작 오프셋은 항상 4로 고정되어 있지만, 끝나는 위치는 인증서의 내용 길이에 따라 달라진다. OpenSSL의 -strparse 옵션을 사용하면 특정 오프셋에서 시작하는 필드를 추출할 수 있다. 오프셋 4를 지정하면 서명 블록을 제외한 인증서 본문을 얻을 수 있다.

추출한 파일의 내용을 cat c0_body.bin을 통해 확인할 수 있다. 인증서의 본문을 추출하면 아래의 커멘드를 이용해서 해시값을 구할 수 있다.


서명 검증

이제 CA의 공개 키, CA의 서명, 그리고 서버 인증서 본문까지 필요한 모든 정보를 확보했다. 이를 바탕으로 직접 작성한 프로그램을 실행해 서명의 유효성을 검증할 수 있다.

from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend

# 서명 검증 함수
def verify_signature(ca_public_key_path, signature_path, data_path):
    # CA 공개키 불러오기
    with open(ca_public_key_path, "rb") as key_file:
        ca_public_key = serialization.load_pem_public_key(
            key_file.read(),
            backend=default_backend()
        )

    # 서명 값 읽기 (16진수 서명 값 변환)
    with open(signature_path, "r") as sig_file:
        signature = bytes.fromhex(sig_file.read().strip())

    # 본문 데이터 읽기
    with open(data_path, "rb") as data_file:
        data = data_file.read()

    # 서명 검증 수행 (RSA-SHA256 사용)
    try:
        ca_public_key.verify(
            signature,
            data,
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        print("서명 검증 성공: 서명이 유효합니다.")
    except Exception as e:
        print("서명 검증 실패: 서명이 유효하지 않습니다.")
        print(f"오류: {e}")

# 파일 경로 설정
ca_public_key_path = "ca_public_key.pem"  # CA 공개키 파일 경로
signature_path = "cleaned_signature"     # 서명 값 파일 경로
data_path = "c0_body.bin"                # 인증서 본문 데이터 경로

# 서명 검증 실행
verify_signature(ca_public_key_path, signature_path, data_path)

 

X.509 인증서에서 서명 검증은 다음의 작업을 포함한다.

  • 인증서 본문 데이터를 해시(Hash)하여 요약 값을 생성
  • 서명 값이 CA의 공개키를 통해 복호화된 값과 비교
  • 이를 통해 해당 서명이 실제로 CA(인증기관)가 서명한 것인지 검증할 수 있음

 

1단계: CA 공개키 불러오기

with open(ca_public_key_path, "rb") as key_file:
    ca_public_key = serialization.load_pem_public_key(
        key_file.read(),
        backend=default_backend()
    )
  • ca_public_key_path: CA의 공개키가 저장된 파일
  • serialization.load_pem_public_key:
    • PEM 형식의 공개키를 파싱하고 불러옴
    • default_backend()는 암호화 작업에 사용되는 기본 백엔드
  • 이 공개키는 서명 검증 과정에서 사용

 

2단계: 서명 값 읽기 및 처리

with open(signature_path, "r") as sig_file:
    signature = bytes.fromhex(sig_file.read().strip())
  • signature_path: CA 서명이 저장된 파일
  • bytes.fromhex():
    • 서명이 16진수 문자열로 저장되어 있기 때문에 이를 바이트 형태로 변환
    • 공백과 줄바꿈이 제거된 상태여야 정상적으로 동작
  • 서명 값은 인증서 본문을 검증하는 데 사용

 

3단계: 인증서 본문 데이터 읽기

with open(data_path, "rb") as data_file:
    data = data_file.read()
  • data_path: 인증서 본문 데이터(c0_body.bin)가 저장된 파일
  • 이 데이터는 서명 검증 전에 SHA256 해시로 요약되어야 하는 원본 데이터

 

4단계: 서명 검증 수행

try:
    ca_public_key.verify(
        signature,  # 서명 값
        data,       # 인증서 본문 데이터
        padding.PKCS1v15(),  # RSA 서명 시 PKCS#1 v1.5 패딩 사용
        hashes.SHA256()      # 해시 알고리즘으로 SHA256 사용
    )
    print("서명 검증 성공: 서명이 유효합니다.")
except Exception as e:
    print("서명 검증 실패: 서명이 유효하지 않습니다.")
    print(f"오류: {e}")
  1. ca_public_key.verify():
    • CA의 공개키를 사용하여 서명 값이 데이터의 SHA256 해시에 대해 유효한지 검증
    • 입력값:
      • signature: CA 서명 값
      • data: 서명 전에 해시된 원본 데이터
      • padding.PKCS1v15(): RSA 서명에서 표준적으로 사용되는 패딩 방식
      • hashes.SHA256(): 해시 알고리즘으로 SHA256을 사용
  2. 검증 로직:
    • 서명 값이 CA의 개인키로 서명된 데이터의 해시와 일치하는지 확인
    • 일치하면 서명 검증 성공, 일치하지 않으면 서명 검증 실패 예외가 발생

 

서명 검증의 원리

  1. 인증서 본문 해시 생성:
    • 데이터(c0_body.bin)를 SHA256 해시 함수로 요약
  2. 서명 값 복호화:
    • CA의 공개키를 사용해 서명 값을 복호화
    • 이 과정에서 복호화된 값은 해시 값이어야 함
  3. 비교:
    • 서명 값에서 복호화된 해시와 새로 계산된 데이터의 SHA256 해시를 비교.
    • 일치하면 서명이 유효하다는 것을 증명

실행 흐름

  1. ca_public_key.pem: CA의 공개 키를 로드
  2. cleaned_signature: 서명 값을 바이트 형태로 변환
  3. c0_body.bin: 인증서의 본문 데이터를 읽어옴
  4. 서명 검증:데이터의 해시를 계산하고, CA 공개 키를 사용하여 서명 값과 비교
  5. 결과에 따라 성공 또는 실패 메시지를 출력

 

최종적으로 검증 코드의 핵심 포인트는 아래와 같다.

1. RSA 기반 서명 검증에는 PKCS#1 v1.5 패딩과 SHA256 해시가 필수

2. 데이터를 해시한 결과와 서명 값을 복호화한 결과를 비교

3. cryptography 라이브러리의 verify 메서드는 이러한 검증 과정을 추상화하여 제공

 

코드를 수행하고 결과를 확인하니 서명검증이 제대로 된것을 볼 수 있다.


728x90