14.1 HTTP를 안전하게 만들기
안전한 HTTP는 아래의 기능들을 제공해야 한다.
- 서버 인증 : 클라이언트는 자신이 위조된 서버가 아닌 진짜 서버와 통신하고 있음을 알 수 있어야 한다.
- 클라이언트 인증 : 서버는 자신이 진짜 클라이언트와 통신함을 알아야 한다.
- 무결성 : 클라이언트와 서버는 그들의 데이터가 위조된 것이 아님을 알 수 있어야 한다.
- 암호화 : 통신되는 데이터는 도청이 되더라도 해독되서는 안된다.
- 효율 : 알고리즘은 충분히 빨라야 한다.
- 편재성 : 프로토콜은 거의 모든 클라이언트와 서버에서 지원되어야 한다.
- 관리상 확장성 : 누구든 어디서든 즉각적인 보안 통신을 할 수 있어야 한다.
- 적응성 : 현재 알려진 최선의 보안 방법을 지원해야 한다.
14.1.1 HTTPS
HTTPS는 HTTP를 안전하게 만드는 방식 중에서 가장 인기 있는 것이다. HTTPS를 사용할 때, HTTP 메시지는 네트워크로 보내지기 전, 암호화 되어 보내진다. HTTPS는 HTTP의 하부에 전송 레벨 암호 보안 계층을 제공함으로써 동작한다. SSL/TLS라고 하는 것이 보안 계층들이다.
(명확하게 최근에 사용되는 보안계층 프로토콜은 TLS 1.2 & TLS 1.3 이다. SSL의 보안 취약성에 의해 권장되지 않는다.)
14.2 디지털 암호학
이 장에서는 HTTPS와 SSL에 사용되는 암호 인코딩 기법에 대한 배경지식을 설명한다.
14.2.5 디지털 암호
기계 장치의 물리적인 금속 키와 달리, 디지털 키는 그저 숫자에 불과하다. 이들 디지털 키 값은 인코딩과 디코딩 알고리즘에 대한 입력값이다. 코딩 알고리즘이란 데이터를 받아 알고리즘 & 키 값에 근거하여 인코딩하거나 디코딩하는 함수를 말한다.
- E : 인코딩 알고리즘
- P : 평문 메시지
- e : 키 값
- C : 암호문
14.3 대칭키 암호법
인코딩할 때 사용하는 키가 디코딩을 할 때와 동일한 암호 방식을, ‘대칭키 암호’라고 말한다. 대칭키 암호에서는 발송자와 수신자 모두 동일한 키를 가지고 있어야 한다. 하나의 공유된 키를 가지고 인코딩과 디코딩을 수행한다. (과거에는 대표전 대칭키 알고리즘으로 DES가 채택됬지만, 현재는 AES 알고리즘을 사용한다.)
14.3.1 키 길이와 열거 공격
무차별로 모든 키 값을 대입해보는 공격을 열거 공격이라고 한다. 키 값이 만약 40비트라면, 2^40개의 키 조합을 생성할 수 있다. 키의 길이를 길게하고, 알고리즘을 복잡하게 함으로써, 열거 공격에 대응할 수 있다.
14.3.2 공유키 발급하기
대칭키 암호의 가장 큰 단점 중 하나는 발송자와 수신자가 모두 하나의 키를 공유하고 있어야 한다는 것이다. 만약 N개의 노드가 있고, 각 노드가 상대 N-1과 보안 통신을 수행하고자 한다면, 서로 공유키를 가지고 있어야 한다. 이 경우, 대략 총 N^2개의 비밀 키가 필요하다.
14.4 공개키 암호법
공개키 암호 방식은 두 개의 비대칭 키를 사용한다. 하나는 호스트의 메시지를 인코딩하기 위한 것이며, 다른 하나는 그 호스트의 메시지를 디코딩하기 위한 것이다. 인코딩 키는 모두를 위해 공개되어 있다. 하지만 호스트만이 개인 디코딩 키를 알고 있다.
위 이미지에서, 클라이언트는 공개키를 이용해 본인의 메시지를 인코딩한 뒤 네트워크에 전달하고, 서버는 넘겨받은 암호문을 본인만이 갖고 있는 개인 디코딩 키를 활용해 디코딩한다.
이러한 방식을 통해, 한 노드는 다른 노드들과 통신하기 위해, 자신의 공개키를 외부에 공개하기만 하면 된다. 이로써, N^2에 해당하던 키를 N개로 줄일 수 있다. 위 이미지에서 공개키를 사용하는 서버는 다른 노드들에게 오직 하나의 공개키만 공유함으로 써 보안 통신을 수행하고 있다.
공개키 암호화 기술은 보안 프로토콜을 전 세계의 모든 컴퓨터 사용자에게 적용하는 것을 가능하게 했다. 표준화된 공개키 기술 묶음을 만드는 것의 중요성 때문에, 거대한 공개 키 인프라(Public-Key Infrastructure, PKI) 표준화 작업이 25년 넘게 계속 진행 중인 상태다. (현재 최신 명세는 RFC 5280이라고 한다.)
14.4.1 RSA
공개키 비대칭 암호는 아래의 아래의 정보가 제공되어도 암호를 크래킹하여 해당 개인 키를 찾아내는 것이 불가능에 가까울 정도로 어렵다고 한다( → 물론, 필자는 양자컴퓨팅에 의해 뚫릴 수 있다는 이야기를 들었다).
- 공개키
- 평문의 일부
- 공개키로 평문을 인코딩하여 얻은 평문에 대한 암호문
- RSA 구현 소스코드
14.4.2 혼성 암호 체계와 세션 키
공개키 암호 방식의 알고리즘은 계산이 느린 경향이 있기 때문에, 실제로는 대칭키 방식과 비대칭키 방식을 섞은 것을 사용한다.
14.5 디지털 서명
디지털 서명을 사용하여 누가 메시지를 썻는지, 그 메시지가 위조되지 않았는지 증명할 수 있다. 디지털 서명은 메시지에 붙어있는 암호 체크섬이다. 이를 통해 아래의 이점을 제공한다.
- 서명은 메시지를 작성한 저자가 누구인지 알려준다. 오직 비밀키를 가지고 있는 저자만이 서명을 만들 수 있기 때문에, 체크섬은 저자의 ‘서명’처럼 동작한다. (비밀키를 통해 암호화된 암호문 만이, 공개키에 의해 정상적으로 복호화될 수 있다.)
- 서명은 메시지 위조를 방지한다. 악의적인 공격자가 메시지를 수정하더라도, 해당 메시지가 더이상 체크섬과 일치하지 않기 때문에, 위조되었다는 것을 수신자가 판단할 수 있다. (악의적인 공격자는 수정된 메시지에 맞게 체크섬을 새롭게 생성할 수 없다. 왜냐하면, 비밀키가 없기 때문이다.)
위 이미지는 송신측에서 디지털 서명을 생성하고, 수신측에서 이를 확인하는 과정을 나타낸다.
- 송신측은, 메시지를 정제하여 고정 길이의 요약(digest)을 만든다.
- 송신측은, 개인키를 사용하여 해당 요약을 암호화 한다. 이때 암호화는 디코더 함수를 통해 이뤄진다. → 수신측이 공개키로 복호하 가능하게 하기 위함
- 송신측은, 생성된 서명과 메시지를 수신측에 전송한다.
- 수신측은, 공개키를 사용하여 서명을 복호화한다. 복호화된 서명이 메시지의 요약과 일치한다면, 메시지는 위조되지 않았으며, 올바른 송신측에서 왔음을 보장할 수 있다.
14.6 디지털 인증서
디지털 인증서(’certs’라고 불려짐)는 신뢰할 수 있는 기관으로부터 보증 받은 사용자나 회사에 대한 정보를 담고 있다.
14.6.2 X.509 v3 인증서
디지털 인증서에 대한 전 세계적인 단일 표준은 없다고 한다. 그러나 대부분의 인증서는 X.509라 불리는 표준화된 서식에 저장한다고 한다. 아래 링크는 IBM과 MS에서 X.509 인증서에 대한 설명을 기술한 글이다. (이를 통해 X.509 v3 인증서가 표준처럼 사용됨으로 유추할 수 있다.)
X.509 인증서는 대부분 아래의 필드를 포함한다.
- 버전 : 인증서가 따르는 X.509 인증서 버전의 번호
- 일련번호 : 인증기관에 의해 생성된 고유한 정수. CA에 의해 각 인증서는 고유한 일련번호를 가져야 함
- 서명 알고리즘 ID : 서명을 위해 사용된 암호 알고리즘
- 인증서 발급자 : 인증서를 발급하고 서명한 기관의 이름
- 유효 기간 : 인증서가 유효한 기간
- 대상의 이름 : 인증서에 기술된, 사람이나 조직과 같은 엔터티
- 대상의 공개 키 정보 : 인증 대상의 공개 키, 알고리즘, 추가 매개변수
아래 내용은 네이버에서 사용하는 디지털 인증서이다. X.509 v3 인증서를 사용하는 것을 알 수 있다.
Basic Fields
serial number: 066541109402820d3403fcec36f7098b
signature algorithm: SHA256withRSA
issuer: /C=US/O=DigiCert Inc/CN=DigiCert TLS RSA SHA256 2020 CA1
notBefore: 230523000000Z
notAfter: 240607235959Z
subject: /C=KR/ST=Gyeonggi-do/L=Seongnam-si/O=NAVER Corp./CN=*.www.naver.com
subject public key info:
key algorithm: RSA
n=00ebaaf56359e804...
e=010001
X509v3 Extensions:
authorityKeyIdentifier :
kid=b76ba2eaa8aa848c79eab4da0f98b2c59576b9f4
subjectKeyIdentifier :
b56d5836bb1876efd583359381bbac856c365098
subjectAltName :
dns: *.www.naver.com
dns: www.naver.com
keyUsage CRITICAL:
digitalSignature,keyEncipherment
extKeyUsage :
serverAuth, clientAuth
cRLDistributionPoints :
<http://crl3.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl>
<http://crl4.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl>
certificatePolicies :
policy oid: 2.23.140.1.2.2
cps: <http://www.digicert.com/CPS>
authorityInfoAccess :
ocsp: <http://ocsp.digicert.com>
caissuer: <http://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1-1.crt>
basicConstraints :
{}
1.3.6.1.4.1.11129.2.4.2 :
signature algorithm: SHA256withRSA
signature: 551c9ddca5d3aa18...
아래 이미지는 ‘네이버’와 ‘NH투자증권’의 웹사이트에서 사용하는 CA Chain을 보여준다. 서로 다른 CA 업체를 사용하고 있다. 브라우저는 기본적으로 Root CA에 해당하는 리스트를 가지고 있다. 해당 체인을 순회하며 인증서의 신뢰성을 확인한다고 한다.
더 자세한 내용은 아래 링크가 RFC 5280을 확인해보자
14.7 HTTPS의 세부사항
14.7.3 보안 전송 셋업
HTTPS에서의 절차는 SSL 보안 계층 때문에 약간 더 복잡하다. HTTPS에서, 클라이언트는 먼저 웹 서버의 443 포트(보안 HTTP의 기본 포트)로 연결한다. TCP 커넥션이 맺어지면, 클라이언트와 서버는 암호법 매개변수와 교환 키를 협상하면서 SSL 계층을 초기화한다. SSL 핸드셰이크가 완료되면, 클라이언트는 SSL 계층에 요청 메시지를 전송한다. 요청 메시지는 전송전 (서버와 공유한 대칭키로) 암호화 된다.
14.7.4 SSL 핸드셰이크
SSL 핸드셰이크에서는 아래의 과정이 진행된다.
- 프로토콜 버전 번호 교환
- 양쪽이 알고 있는 암호 선택
- 양쪽의 신원을 입증
- 채널을 암호화하기 위한 임시 세션 키 생성
SSL 핸드셰이크
클라이언트와 서버가 SSL 계층을 초기화하기 위해 구체적으로는 아래와 같은 절차를 거친다.
구체적인 예시를 위해 와이어샤크(패킷 캡처 도구), 자바 SSL 소켓 통신을 활용한다.
- 클라이언트 헬로우 : 클라이언트가 먼저 서버에게 SSL 요청을 보낸다. 이때 클라이언트는, 자신이 사용중인 TLS 버전, 암호화 방식 모음(cipher suite), 랜덤으로 생성한 값 등을 함께 포함한다.
javax.net.ssl|DEBUG|10|main|2024-01-09 22:21:45.184 KST|ClientHello.java:641|Produced ClientHello handshake message (
"ClientHello": {
"client version" : "TLSv1.2",
"random" : "661591EC720CB6464E1E52111DD2A28EA0001A735908817F420866D14A2C2C93",
"session id" : "A376AE28002515F4252E3CBD8563F3B06B2926AA74007FA46149AB3C4447AA54",
"cipher suites" : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303)]",
"compression methods" : "00",
"extensions" : [
"server_name (0)": {
type=host_name (0), value=www.naver.com
},
"status_request (5)": {
"certificate status type": ocsp
"OCSP status request": {
"responder_id": <empty>
"request extensions": {
<empty>
}
}
},
"supported_groups (10)": {
"versions": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
},
"signature_algorithms (13)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, ed25519, ed448, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"supported_versions (43)": {
"versions": [TLSv1.3]
},
"psk_key_exchange_modes (45)": {
"ke_modes": [psk_dhe_ke]
},
"signature_algorithms_cert (50)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, ed25519, ed448, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"key_share (51)": {
"client_shares": [
{
"named group": x25519
"key_exchange": {
0000: B8 D6 57 2E BC A4 3B 30 C4 61 09 A5 47 07 90 A5 ..W...;0.a..G...
0010: B5 F4 69 40 13 A2 45 E7 AD 55 61 0E B7 80 79 2E ..i@..E..Ua...y.
}
},
{
"named group": secp256r1
"key_exchange": {
0000: 04 8D 67 D2 33 E1 91 40 62 25 58 BE EA 3A C7 35 ..g.3..@b%X..:.5
0010: B8 E2 5A AD 2F 7E EC 76 04 A0 99 CA 53 44 B4 1C ..Z./..v....SD..
0020: B0 15 23 D8 A9 52 68 E5 4A 4D 13 7A 0C 0F 92 12 ..#..Rh.JM.z....
0030: 52 DE 81 0C AB 3C A2 11 54 14 F4 EE 20 B2 BE B2 R....<..T... ...
0040: FD
}
},
]
}
]
}
)
2. 서버 헬로우 : 서버는 클라이언트의 SSL 연결 요청(클라이언트 헬로우)에 응답하며, 다음의 정보를 클라이언트에게 전송한다. 클라이언트가 전송한 암호 방식 중, 서버가 지원하고 선택한 암호화 방식. 서버의 공개키가 담긴 SSL 인증서. 서버가 생성한 임의의 난수. 등등.
"ServerHello": {
"server version" : "TLSv1.2",
"random" : "2A9A8346228D482F99AFAF23E50D45BA958EAD0814311922F00DB2FFEB226F73",
"session id" : "A376AE28002515F4252E3CBD8563F3B06B2926AA74007FA46149AB3C4447AA54",
"cipher suite" : "TLS_AES_256_GCM_SHA384(0x1302)",
"compression methods" : "00",
"extensions" : [
"supported_versions (43)": {
"selected version": [TLSv1.3]
},
"key_share (51)": {
"server_share": {
"named group": x25519
"key_exchange": {
0000: E0 03 05 0D 36 9B B2 BE 3B 1A 77 5F AE 77 B8 D2 ....6...;.w_.w..
0010: 7A C7 DC 00 B5 2F CE 2C 16 37 27 43 60 31 E7 22 z..../.,.7'C`1."
}
},
}
]
}
)
2-1. 서버의 Certificate hanshake 메시지 : 서버는 클라이언트에게 자신의 SSL 인증서를 전송한다. 전송되는 SSL 인증서는 서버의 개인키로 암호화되기 때문에, 와이어샤크에서 해당 패킷을 캡처 했을 때, 암호화된 데이터가 확인된다. 아래 텍스트는 자바 프로그램에서 확인한 복호화된 SSL 인증서이다.
"Certificate": {
"certificate_request_context": "",
"certificate_list": [
{
"certificate" : {
"version" : "v3",
"serial number" : "066541109402820D3403FCEC36F7098B",
"signature algorithm": "SHA256withRSA",
"issuer" : "CN=DigiCert TLS RSA SHA256 2020 CA1, O=DigiCert Inc, C=US",
"not before" : "2023-05-23 09:00:00.000 KST",
"not after" : "2024-06-08 08:59:59.000 KST",
"subject" : "CN=*.www.naver.com, O=NAVER Corp., L=Seongnam-si, ST=Gyeonggi-do, C=KR",
"subject public key" : "RSA",
"extensions" : [
{
ObjectId: 1.3.6.1.4.1.11129.2.4.2 Criticality=false
},
{
ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: <http://ocsp.digicert.com>
,
accessMethod: caIssuers
accessLocation: URIName: <http://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1-1.crt>
]
]
},
{
ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: B7 6B A2 EA A8 AA 84 8C 79 EA B4 DA 0F 98 B2 C5 .k......y.......
0010: 95 76 B9 F4 .v..
]
]
},
{
ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
},
{
ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: <http://crl3.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl>]
, DistributionPoint:
[URIName: <http://crl4.digicert.com/DigiCertTLSRSASHA2562020CA1-4.crl>]
]]
},
{
ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: [2.23.140.1.2.2]
[PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.1
qualifier: 0000: 16 1B 68 74 74 70 3A 2F 2F 77 77 77 2E 64 69 67 ..<http://www.dig>
0010: 69 63 65 72 74 2E 63 6F 6D 2F 43 50 53 icert.com/CPS
]] ]
]
},
{
ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]
},
{
ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_Encipherment
]
},
{
ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: *.www.naver.com
DNSName: www.naver.com
]
},
{
ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: B5 6D 58 36 BB 18 76 EF D5 83 35 93 81 BB AC 85 .mX6..v...5.....
0010: 6C 36 50 98 l6P.
]
]
}
]}
"extensions": {
"status_request (5)": {
"certificate status response type": ocsp
"OCSP status response": {
OCSP Response:
Response Status: SUCCESSFUL
Responder ID: byKey: B76BA2EAA8AA848C79EAB4DA0F98B2C59576B9F4
Produced at: Tue Jan 09 00:36:37 KST 2024
1 response:
SingleResponse:
CertId
Algorithm: SHA-1
issuerNameHash
0000: E4 E3 95 A2 29 D3 D4 C1 C3 1F F0 98 0C 0B 4E C0 ....).........N.
0010: 09 8A AB D8
issuerKeyHash:
0000: B7 6B A2 EA A8 AA 84 8C 79 EA B4 DA 0F 98 B2 C5 .k......y.......
0010: 95 76 B9 F4
SerialNumber: [ 06654110 9402820d 3403fcec 36f7098b]
CertStatus: GOOD
thisUpdate is Tue Jan 09 00:21:02 KST 2024
nextUpdate is Mon Jan 15 23:21:02 KST 2024
}
}
}
},
{
"certificate" : {
"version" : "v3",
"serial number" : "06D8D904D5584346F68A2FA754227EC4",
"signature algorithm": "SHA256withRSA",
"issuer" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"not before" : "2021-04-14 09:00:00.000 KST",
"not after" : "2031-04-14 08:59:59.000 KST",
"subject" : "CN=DigiCert TLS RSA SHA256 2020 CA1, O=DigiCert Inc, C=US",
"subject public key" : "RSA",
"extensions" : [
{
ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: <http://ocsp.digicert.com>
,
accessMethod: caIssuers
accessLocation: URIName: <http://cacerts.digicert.com/DigiCertGlobalRootCA.crt>
]
]
},
{
ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 03 DE 50 35 56 D1 4C BB 66 F0 A3 E2 1B 1B C3 97 ..P5V.L.f.......
0010: B2 3D D1 55 .=.U
]
]
},
{
ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:0
]
},
{
ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: <http://crl3.digicert.com/DigiCertGlobalRootCA.crl>]
]]
},
{
ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: [2.16.840.1.114412.2.1]
[] ]
[CertificatePolicyId: [2.23.140.1.1]
[] ]
[CertificatePolicyId: [2.23.140.1.2.1]
[] ]
[CertificatePolicyId: [2.23.140.1.2.2]
[] ]
[CertificatePolicyId: [2.23.140.1.2.3]
[] ]
]
},
{
ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]
},
{
ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_CertSign
Crl_Sign
]
},
{
ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: B7 6B A2 EA A8 AA 84 8C 79 EA B4 DA 0F 98 B2 C5 .k......y.......
0010: 95 76 B9 F4 .v..
]
]
}
]}
"extensions": {
}
},
{
"certificate" : {
"version" : "v3",
"serial number" : "083BE056904246B1A1756AC95991C74A",
"signature algorithm": "SHA1withRSA",
"issuer" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"not before" : "2006-11-10 09:00:00.000 KST",
"not after" : "2031-11-10 09:00:00.000 KST",
"subject" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"subject public key" : "RSA",
"extensions" : [
{
ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 03 DE 50 35 56 D1 4C BB 66 F0 A3 E2 1B 1B C3 97 ..P5V.L.f.......
0010: B2 3D D1 55 .=.U
]
]
},
{
ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen: no limit
]
},
{
ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_CertSign
Crl_Sign
]
},
{
ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 03 DE 50 35 56 D1 4C BB 66 F0 A3 E2 1B 1B C3 97 ..P5V.L.f.......
0010: B2 3D D1 55 .=.U
]
]
}
]}
"extensions": {
}
},
]
}
3. 클라이언트는 서버의 서버의 SSL 인증서를 검증한다. CA 인증서 리스트에서를 활용하여 서버의 SSL 인증서를 검사한다. CA 공개키를 사용하여 암호화된 서버의 SSL 인증서를 복호화하여, 해당 인증서가 실제 서버의 인증서가 맞는지 확인한다.
javax.net.ssl|DEBUG|10|main|2024-01-09 22:21:45.306 KST|X509TrustManagerImpl.java:247|Found trusted certificate (
"certificate" : {
"version" : "v3",
"serial number" : "083BE056904246B1A1756AC95991C74A",
"signature algorithm": "SHA1withRSA",
"issuer" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"not before" : "2006-11-10 09:00:00.000 KST",
"not after" : "2031-11-10 09:00:00.000 KST",
"subject" : "CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US",
"subject public key" : "RSA",
"extensions" : [
{
ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 03 DE 50 35 56 D1 4C BB 66 F0 A3 E2 1B 1B C3 97 ..P5V.L.f.......
0010: B2 3D D1 55 .=.U
]
]
},
{
ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen: no limit
]
},
{
ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_CertSign
Crl_Sign
]
},
{
ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 03 DE 50 35 56 D1 4C BB 66 F0 A3 E2 1B 1B C3 97 ..P5V.L.f.......
0010: B2 3D D1 55 .=.U
]
]
}
]}
)
이후의 과정은 TLS 1.2 & TLS 1.3 가 서로 다르다.
TLS 1.2
4. 클라이언트는 자신이 생성한 난수와 서버의 난수를 사용하여 premaster 비밀키를 생성한다. 서버의 인증서를 해당 비밀키를 암호화하여 서버에게 전송한다.
5. 서버는 자신의 비밀키로, 클라이언트가 공개키로 암호화해서 전송한 premaster 비밀키를 복호화한다. 서버는 클라이언트가 전달한 난수, 서버가 생성한 난수, 방금 전 클라이언트가 전송한 premaster 비밀키를 조합하여 세션 키를 새롭게 생성한다. 이 세션 키는 클라이언트와 서버간의 대칭키로 활용될 것이며, 위 모든 과정은 이 대칭키를 만들기 위한 과정이였다. 이때, 클라이언트와 서버에서의 동일한 로직으로 세션 키를 생성한다.
6. 서버는 클라이언트에게 Finished handshake 메시지를 전송한다. 이 메시지는 세션 키로 암호화 된다.
7. 클라이언트는 서버에게 전달받은 Finished handshake 메시지를 세션 키로 복호화하여 내용을 확인한다. 그리고, 서버에게 Finished handshake 메시지를 전송한다. 마찬가지로 세션 키로 암호화되어 전송된다.
8. 끝! 이제 세션 키를 사용하여 서버와 클라이언트의 보안 통신이 가능해졌다.
TLS 1.3
TLS 1.2와 다르게, TLS 1.3에서는 클라이언트가 서버에게 premaster 비밀키를 전달하지 않는다. 클라이언트의 헬로우를 받은 시점에, 서버는 세션 키(대칭 키) 생성을 시작한다. 어떻게 그게 가능할까?
위 이미지는 맨 처음 수행된 클라이언트 헬로우 의 메시지이다. 하단에 보면 Extension들이 많은 것을 볼 수 있다. TLS 1.3에서는 저런 Extension들에 있는 정보들을 활용하여, premaster key 생성 없이, 1번의 통신으로 세션 키를 생성하도록 지원한다. 이러한 방법은 고정된 것은 아니며, 어떤 키 교환 방법을 채택하냐에 따라서 달라진다고 한다.
더 깊게 파고들면, 복잡한 내용들이 많기 때문에 여기까지 설명하기로 한다. 더 자세한 내용은 아래 링크를 참조하면 좋을 듯 하다.
자바 SSL 소켓 예제
public class SSLClient {
public static void main(String[] args){
try {
System.setProperty("javax.net.debug", "all");
SocketFactory socketFactory = SSLSocketFactory.getDefault();
SSLSocket kkSocket = (SSLSocket) socketFactory.createSocket(InetAddress.getByName("www.naver.com"), 443);
kkSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), false);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String fromServer;
out.println("GET /\\r\\n");
out.println("Accept-Encoding: br,deflate,gzip,x-gzip\\r\\n");
out.println("Content-Language: ko\\r\\n\\r\\n");
out.flush();
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
참고자료
'CS > 네트워크' 카테고리의 다른 글
[HTTP] 15. 엔터티와 인코딩 (0) | 2024.01.16 |
---|---|
[HTTP] 11. 클라이언트 식별과 쿠키 (0) | 2024.01.16 |
[HTTP] 12. 기본 인증 (0) | 2024.01.09 |
[HTTP] 10. HTTP/2.0 (2) | 2024.01.08 |
[HTTP] 07. 캐시 (2) (2) | 2024.01.08 |