ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Ribbon SBC] Core–Edge TLS/SRTP Hands on - 3 : 공인 도메인 없이, 사설 CA와 SAN 인증서 만들기
    Ribbon Communications/Session Border controller 2026. 9. 15. 14:09

    그림으로 보는 전체 흐름

    Root CA를 등록하고 Core/Edge 인증서를 TLS 프로파일에 연결하는 순서

     

    1편 · 전체 SOP와 실습 첨부 | 이전 편

    이번 편에서 할 일

    Core와 Edge가 TLS로 서로를 믿으려면, 둘이 같이 신뢰하는 발급자(CA)와 각자의 장비 인증서가 필요합니다. 이 랩의 도메인 sbc.test는 공인 도메인이 아니라서 공인 CA에서 인증서를 받을 수 없습니다. 그래서 이번 편에서는 랩 전용 사설 CA를 새로 만들고, 그 CA로 Core와 Edge 인증서를 발급하겠습니다.

    이전 랩에서 쓰던 CA는 1편에서 두 장비의 신뢰 목록에서 모두 지웠습니다. 이번에 만드는 CA는 이름부터 새로 붙였습니다.

    시험 환경

    항목 값 비고
    스크립트 언어 Python + cryptography 패키지 Windows 호스트 / uv로 cryptography 실행
    작업일 2026-09-15 인증서 유효 시작일은 이틀 앞당겨 2026-09-13

    이름부터 정한다

    항목 Core Edge
    FQDN core.sbc.test edge.sbc.test
    인증서 Subject CN core.sbc.test edge.sbc.test
    SAN (DNS) core.sbc.test edge.sbc.test
    데이터 IP (TLS 구간) 10.77.20.1 10.77.20.2
    장비에 등록한 이름 FRESH_CORE (local), CA는 FRESH_CA (remote) Supplementary Certificate ID 1002, Trusted CA ID 2

    이름 검증은 요즘 대부분 CN보다 SAN을 봅니다. 그래서 CN과 SAN에 같은 FQDN을 넣었습니다. IP SAN은 넣지 않았습니다. 이름 검증을 FQDN으로 하겠다는 설계를 흐리지 않기 위해서입니다.

    신뢰 관계

    개념도(설계)

                     SBC Fresh Lab Root CA  (자체 서명, RSA 3072, 10년)
                       │                         │
                서명   │                         │  서명
                       ▼                         ▼
          core.sbc.test (RSA 2048, 1년)    edge.sbc.test (RSA 2048, 1년)
             개인키: Core에만                  개인키: Edge에만
                       │                         │
       Core 신뢰 저장소: FRESH_CA        Edge Trusted CA: SBC Fresh Lab Root CA
    

    원칙은 세 가지입니다.

    1. CA 인증서(공개)는 양쪽 모두에 넣습니다. 상대가 내민 인증서를 검증하는 기준이 됩니다.
    2. 장비 개인키는 그 장비에만 넣습니다. Core 개인키를 Edge에 복사하지 않고, 그 반대도 마찬가지입니다.
    3. CA 개인키는 어떤 장비에도 넣지 않습니다. 발급할 때만 쓰고 따로 보관합니다.

    누가 TLS client이고 누가 server인가

    TLS에서는 연결을 먼저 여는 쪽이 client입니다. 이번 시험의 정방향 호(2000번)는 Core → Edge 방향입니다. 그래서 Core가 TLS client, Edge가 TLS server가 됩니다. 7편 캡처에서도 ClientHello는 10.77.20.1(Core)에서, ServerHello는 10.77.20.2(Edge)에서 나왔습니다.

    역방향(1000번, Edge → Core) 호라면 새 연결에서는 역할이 뒤바뀔 수 있습니다. 두 인증서에 serverAuth와 clientAuth를 모두 넣은 이유입니다. 역방향 호는 구성만 넣었고 시험하지는 않았습니다.

    상호 인증(mutual TLS)이면 server도 client에게 인증서를 요구합니다. 그러면 양쪽 모두 상대 인증서를 CA로 검증하고, 이름까지 확인합니다.

    검증 Core가 하는 일 (client) Edge가 하는 일 (server)
    상대 인증서 서명 검증 Edge 인증서가 FRESH_CA로 서명됐는지 Core 인증서가 Trusted CA로 서명됐는지
    상대 이름 검증 Edge 인증서 이름이 tlsPeerName edge.sbc.test와 맞는지 Validate Client FQDN
    자기 인증서 제시 clientCertName FRESH_CORE Certificate = ID 1002

    스크립트 원문

    어디서: CA 작업용 컴퓨터(SBC 밖)

    아래가 이번에 실행한 스크립트 원문입니다. 파일명은 create-fresh-ca.py입니다.

    from pathlib import Path
    import datetime,secrets
    from cryptography import x509
    from cryptography.x509.oid import NameOID,ExtendedKeyUsageOID
    from cryptography.hazmat.primitives import hashes,serialization
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives.serialization import pkcs12,PrivateFormat
    B=Path(__file__).resolve().parent/'new-ca-output'
    B.mkdir(exist_ok=False)
    T=B/'public';T.mkdir()
    (B/'secrets').mkdir()
    S=B/'secrets/tls';S.mkdir(exist_ok=True)
    if not (S/'ca.key').exists():
     now=datetime.datetime.now(datetime.timezone.utc); key=rsa.generate_private_key(public_exponent=65537,key_size=3072)
     name=x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,'SBC Fresh Lab Root CA')])
     ca=x509.CertificateBuilder().subject_name(name).issuer_name(name).public_key(key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(days=2)).not_valid_after(now+datetime.timedelta(days=3650)).add_extension(x509.BasicConstraints(ca=True,path_length=0),True).add_extension(x509.KeyUsage(False,False,False,False,False,True,True,False,False),True).add_extension(x509.SubjectKeyIdentifier.from_public_key(key.public_key()),False).sign(key,hashes.SHA256())
     (S/'ca.key').write_bytes(key.private_bytes(serialization.Encoding.PEM,PrivateFormat.PKCS8,serialization.NoEncryption()))
     (T/'ca.pem').write_bytes(ca.public_bytes(serialization.Encoding.PEM))
     import secrets
     pw=secrets.token_hex(10);(S/'p12-password.txt').write_text(pw)
     for host in ['core','edge']:
      k=rsa.generate_private_key(public_exponent=65537,key_size=2048);fqdn=host+'.sbc.test'
      cert=x509.CertificateBuilder().subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,fqdn)])).issuer_name(ca.subject).public_key(k.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(days=2)).not_valid_after(now+datetime.timedelta(days=365)).add_extension(x509.BasicConstraints(ca=False,path_length=None),True).add_extension(x509.SubjectAlternativeName([x509.DNSName(fqdn)]),False).add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH,ExtendedKeyUsageOID.CLIENT_AUTH]),False).add_extension(x509.KeyUsage(True,False,True,False,False,False,False,False,False),True).add_extension(x509.SubjectKeyIdentifier.from_public_key(k.public_key()),False).add_extension(x509.AuthorityKeyIdentifier.from_issuer_public_key(key.public_key()),False).sign(key,hashes.SHA256())
      (T/(host+'.pem')).write_bytes(cert.public_bytes(serialization.Encoding.PEM));(S/(host+'.key')).write_bytes(k.private_bytes(serialization.Encoding.PEM,PrivateFormat.PKCS8,serialization.NoEncryption()))
      enc=PrivateFormat.PKCS12.encryption_builder().kdf_rounds(50000).key_cert_algorithm(pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC).hmac_hash(hashes.SHA1()).build(pw.encode())
      (S/(host+'.p12')).write_bytes(pkcs12.serialize_key_and_certificates(fqdn.encode(),k,cert,[ca],enc))
     print('Created private lab CA and two DNS SAN certificates. Private material stored under secrets/tls.')
    else:print('Using existing lab TLS certificate files.')
    

    코드가 한 줄에 몰려 있어서 읽기 어렵습니다. 그래서 아래에 부분별로 풀어 보겠습니다. 게시한 코드와 실행한 코드를 똑같이 두려고 줄바꿈 정리는 하지 않았습니다. import secrets가 두 번 나오지만 동작에는 영향이 없습니다. 첨부 스크립트 SHA256: d0b3a97ac3abb7a67b2c4960131bd1b2edc89ef69d2570c64c49751ff85753aa

    출력 폴더를 만드는 부분

    코드 하는 일 왜
    B.mkdir(exist_ok=False) new-ca-output 폴더를 새로 만들고, 이미 있으면 오류로 중단 실수로 다시 실행해서 기존 CA·개인키를 덮어쓰는 일을 막음
    T=B/'public' 공개 파일 폴더 장비·글에 올려도 되는 인증서만 모음
    S=B/'secrets/tls' 비공개 파일 폴더 개인키, P12, 암호 파일
    if not (S/'ca.key').exists() CA 키가 없을 때만 발급 새 폴더라 항상 참이지만, 기존 CA 재사용 분기를 남겨 둔 형태

    CA 인증서

    필드 값 왜 이 값인가
    키 RSA 3072비트, 공개 지수 65537 CA는 장비 인증서보다 오래 쓰니 한 단계 긴 키를 선택
    Subject = Issuer CN=SBC Fresh Lab Root CA 자체 서명 루트. 이전 랩 CA와 이름이 겹치지 않게
    유효 시작 실행 시각 − 2일 장비 시계가 조금 뒤처져도 "아직 유효하지 않음"으로 거부되지 않게
    유효 종료 실행 시각 + 3650일 랩을 오래 둘 수 있게 10년
    Basic Constraints CA:TRUE, pathlen:0, critical CA로 쓰되 하위 CA를 만들 수 없음. 장비 인증서만 발급
    Key Usage keyCertSign, cRLSign, critical 인증서 서명과 CRL 서명 용도로만
    Subject Key Identifier 공개키에서 계산 장비 인증서의 Authority Key Identifier와 짝을 맞춰 체인 연결을 명확히
    서명 SHA-256 —

    x509.KeyUsage(False,False,False,False,False,True,True,False,False)는 인자 순서가 digital_signature, content_commitment, key_encipherment, data_encipherment, key_agreement, key_cert_sign, crl_sign, encipher_only, decipher_only입니다. 그래서 6번째와 7번째만 켜진 것입니다.

    장비 인증서 (Core·Edge 공통 규칙)

    필드 값 왜 이 값인가
    키 RSA 2048비트 5편에서 볼 Edge 화면에도 Key Length 2048로 표시됨
    Subject CN=core.sbc.test / CN=edge.sbc.test FQDN 그대로
    Issuer CA의 Subject —
    유효 기간 실행 시각 − 2일 ~ + 365일 CA와 같은 이유로 시작일을 앞당김, 랩용으로 1년
    Basic Constraints CA:FALSE, critical 이 인증서로 다른 인증서를 발급하지 못하게
    SAN DNS:core.sbc.test / DNS:edge.sbc.test 이름 검증의 실제 기준
    Extended Key Usage serverAuth, clientAuth 한 장비가 TLS server도, client도 될 수 있음. 특히 상호 인증에서는 client 인증서에 clientAuth가 필요
    Key Usage digitalSignature, keyEncipherment, critical 이번 협상 암호군 ECDHE-RSA에서는 RSA 키로 핸드셰이크에 서명함. keyEncipherment는 RSA 키 교환 방식과의 호환을 위해 함께 켬
    SKI / AKI 각자 공개키 / CA 공개키에서 계산 체인 연결
    서명 SHA-256 —

    P12 포장

    장비에 개인키와 인증서를 함께 넣으려고 PKCS#12(P12) 파일로 묶었습니다.

    항목 값 왜
    들어 있는 것 장비 개인키 + 장비 인증서 + CA 인증서 장비가 체인까지 한 번에 받게
    friendly name FQDN(core.sbc.test 등) 장비 화면에서 어떤 키인지 알아보기 쉽게
    키·인증서 암호화 PBES1 SHA1 + 3DES-CBC 오래된 import 구현과도 호환되는 방식을 일부러 선택
    MAC 해시 SHA-1 같은 호환성 이유
    KDF 반복 50000 —
    P12 암호 secrets.token_hex(10) → 16진수 20자, Core·Edge 공통 랩 편의. 이 글에는 싣지 않습니다

    PBES1/3DES/SHA-1 조합은 요즘 기준으로 강한 포장 방식이 아닙니다. 그래도 이 랩에서는 장비 import가 되는 것을 우선했습니다. 결과적으로 이번 재구축에서 Core(FRESH_CORE)와 Edge(Supplementary Certificate ID 1002) 모두 이 P12로 import에 성공했습니다. Edge 쪽 등록 결과는 5편 화면에서 볼 수 있습니다. Core 쪽은 7편에서 FRESH_CORE를 쓰는 상호 TLS 호가 성립한 것으로 간접 확인했습니다.

    사설 CA와 두 SBC 인증서 관계

    만들어지는 파일과 공개 범위

    파일 폴더 공개 여부 쓰는 곳
    ca.pem public 공개 가능 Core FRESH_CA, Edge Trusted CA
    core.pem public 공개 가능 파일 검사용
    edge.pem public 공개 가능 파일 검사용
    ca.key secrets/tls 비공개 발급할 때만
    core.key, edge.key secrets/tls 비공개 P12 안에 포함됨
    core.p12, edge.p12 secrets/tls 비공개 Core FRESH_CORE, Edge Supplementary Certificate
    p12-password.txt secrets/tls 비공개 P12 import 때 입력

    생성 스크립트는 개인키를 암호 없는 PEM으로, 장비 import용 묶음을 비밀번호가 있는 P12로 만듭니다. 본 첨부에는 생성 스크립트와 실제 공개 인증서를 넣었습니다. 재현 시 생성된 p12-password.txt 값을 장비 import에 사용합니다.

    Core에 올릴 때는 파일명을 바꿔서 fresh-ca.pem, fresh-core.p12로 넣었습니다. 4편의 CLI 명령에 이 이름이 나옵니다.

    실행

    어디서: CA 작업용 컴퓨터, 스크립트가 있는 폴더

    python create-fresh-ca.py
    

    정상적으로 끝나면 마지막 줄에 아래 한 줄이 출력됩니다(스크립트의 print 문).

    Created private lab CA and two DNS SAN certificates. Private material stored under secrets/tls.
    

    실패 시

    • FileExistsError가 나면 new-ca-output 폴더가 이미 있다는 뜻입니다. 의도한 동작입니다. 기존 CA를 버리고 새로 만들 생각이라면 폴더를 옮긴 뒤 다시 실행하세요.
    • ModuleNotFoundError: cryptography라면 패키지를 먼저 설치해야 합니다.

    만든 인증서를 눈으로 검사

    어디서: openssl 명령이 있는 컴퓨터, public 폴더가 있는 위치

    openssl x509 -in public/ca.pem   -noout -subject -issuer -dates -ext basicConstraints,keyUsage
    openssl x509 -in public/core.pem -noout -subject -issuer -dates -ext subjectAltName,extendedKeyUsage,keyUsage,basicConstraints
    openssl x509 -in public/edge.pem -noout -subject -issuer -dates -ext subjectAltName,extendedKeyUsage,keyUsage,basicConstraints
    openssl verify -CAfile public/ca.pem public/core.pem public/edge.pem
    
    확인 항목 기대값 이유
    CA basicConstraints CA:TRUE, pathlen:0 CA로 쓸 수 있어야 함
    장비 인증서 subjectAltName DNS:core.sbc.test / DNS:edge.sbc.test 이름 검증 기준
    extendedKeyUsage TLS Web Server Authentication, TLS Web Client Authentication 양방향 역할
    issuer CN = SBC Fresh Lab Root CA 체인
    notBefore 장비 현재 시각보다 이전 유효기간 검사
    openssl verify 두 파일 모두 OK CA로 서명이 검증됨

    위 표는 파일 검사 시 비교할 기대값입니다. 실제 신규 호에서는 두 장비가 제시한 인증서의 issuer가 모두 SBC Fresh Lab Root CA였고, Edge의 CertificateRequest와 Core의 CertificateVerify를 캡처에서 확인했습니다. 첨부 mutual-tls-proof.json에 실제 인증서 SHA256이 있습니다.

    장비에 들어간 뒤 보이는 값으로 대조

    파일 검사 출력과 별개로, Edge에 import한 뒤 WebUI 목록에 표시된 값을 스크립트 설계와 맞춰 봤습니다. 화면 자체는 5편에 실었습니다.

    항목 스크립트 설계 Edge 화면 실측(2026-09-15) 판정
    CA Common Name / Issuer SBC Fresh Lab Root CA (자체 서명) SBC Fresh Lab Root C... / SBC Fresh Lab Root C... (목록에서 잘려 표시) 일치
    CA 유효 시작 / 만료 실행일 −2일 / +3650일 Sep 13, 2026 / Sep 12, 2036 일치
    CA Key Length 3072 3072 일치
    Edge 인증서 Common Name edge.sbc.test edge.sbc.test 일치
    Edge 인증서 Issuer CA SBC Fresh Lab Root C... 일치
    Edge 인증서 유효 시작 / 만료 실행일 −2일 / +365일 Sep 13, 2026 / Sep 15, 2027 일치
    Edge 인증서 Key Length 2048 2048 일치

    실행일이 2026-09-15이므로 계산은 이렇습니다.

    • 시작: 09-15 − 2일 = 09-13
    • CA 만료: 2026-09-15 + 3650일 = 2036-09-12 (그 사이 윤년 2028·2032·2036년 2월 29일이 3번 들어가서 09-15보다 3일 앞선 날짜)
    • 장비 만료: 2026-09-15 + 365일 = 2027-09-15

    시각 동기화

    인증서 유효 시작일을 이틀 앞당긴 것은 장비 시계가 약간 어긋나도 버티기 위한 여유입니다. 그렇다고 시계를 확인하지 않아도 된다는 뜻은 아닙니다.

    이번 시험에서 참고할 만한 관찰이 하나 있었습니다. 7편의 첫 호에서 Edge 내부 캡처의 시각과 Rocky 캡처의 시각을 비교해 보면, 같은 INVITE가 Edge 기준으로는 약 1.2초 먼저 나간 것으로 찍혀 있습니다. 네트워크 지연을 거의 0으로 보면, 두 시계가 1초 남짓 어긋나 있다는 뜻입니다. 이틀 여유에 비하면 아주 작은 차이라 인증서 검증에는 영향이 없었습니다. 하지만 캡처 파일끼리 시각을 맞춰 볼 때는 조심해야 합니다.

    확인

    확인 항목 기대값 실측값 판정 증적
    Edge 인증서 CN edge.sbc.test edge.sbc.test 일치 ../assets/13-edge-sip-certificate.png
    Edge 인증서 발급자 SBC Fresh Lab Root CA SBC Fresh Lab Root C... 일치 같은 화면
    CA 유효기간 2026-09-13 ~ 2036-09-12 Sep 13, 2026 ~ Sep 12, 2036 일치 ../assets/12-edge-ca.png
    Edge 인증서 유효기간 2026-09-13 ~ 2027-09-15 Sep 13, 2026 ~ Sep 15, 2027 일치 ../assets/13-edge-sip-certificate.png
    P12 import (Edge) 성공 목록에 ID 1002로 등록 일치 같은 화면
    P12 import (Core) 성공 FRESH_CORE로 상호 TLS 호 성립(간접) 일치(간접) 7편
    SAN·EKU 파일 검사 위 표 —   —

    정리

    새 사설 CA SBC Fresh Lab Root CA를 만들고, 그 CA로 core.sbc.test와 edge.sbc.test 인증서를 발급했습니다. 두 인증서에는 SAN, serverAuth·clientAuth EKU를 넣었습니다. 장비에는 P12로 개인키와 체인을 함께 넣고, CA 인증서만 신뢰 목록에 올립니다. 개인키와 P12 암호는 글에 싣지 않았습니다.

    다음 편에서는 SWe Core에 이 인증서를 등록하는 것부터 시작해, 호 경로 전체를 7번의 commit으로 나눠 만들겠습니다.

    1편 · 전체 SOP와 실습 첨부 | 이전 편

    댓글

Designed by Tistory.