보안/전자 서명 / / 2025. 7. 21. 18:02

CMS SignedData 정리 : 개념 및 구조부터 동작 원리와 활용 예시까지

1. 디지털 전자 서명의 신뢰 기반 - SignedData의 역할

디지털 환경에서의 신뢰는 보안의 핵심 기반이다. 우리는 일상적으로 HTTPS 웹사이트에 접속하고, 인증서 기반의 시스템을 통해 사용자 인증을 수행한다. 그렇다면 사용자는 어떻게 "이 서명이 유효하다" 또는 "이 메시지는 위변조되지 않았다"고 확신할 수 있을까?

 

예를 들어, 사용자가 인증서 기반 로그인 시스템을 통해 민감한 정보를 서버에 제출한다고 가정하자. 이때 서버는 해당 정보가 실제 사용자가 보낸 것인지, 전송 중에 누군가에 의해 변조되지 않았는지를 검증해야 한다. 이러한 검증 과정에서 전자 서명이 사용된다. 그러나 여기서 다시 질문이 생긴다. "단순히 전자 서명 값이 존재한다는 사실만으로, 메시지의 무결성과 진위를 어떻게 보장할 수 있을까?"

 

이 문제를 해결하기 위해 사용되는 표준 구조가 CMS(Cryptographic Message Syntax)의 컨텐츠 유형 중 하나인 SignedData이다. 이 구조는 메시지를 해시한 후, 그 해시값에 서명하고 해당 서명과 관련된 인증서를 함께 포함시켜 전송하는 방식이다. 수신자는 이 SignedData에 포함된 서명값과 인증서를 검증함으로써, 메시지가 위변조되지 않았고 서명자가 신뢰 가능한 인물이라는 사실을 확인할 수 있다.

 

SignedData는 단순히 메시지를 포장하는 기술 구조에 그치지 않는다. 실제 공개 키 기반 구조(PKI, Public Key Infrastructure) 기반의 보안 환경에서는 다음과 같은 다양한 신뢰 절차와 맞물려 동작한다.

 

 

이러한 전체적인 신뢰 체계에서 SignedData메시지의 무결성서명자의 정당성을 입증하는 핵심 요소로 작동한다. 만약 이 구조가 없다면, 전자 서명만으로는 그 유효성과 신뢰성을 보장하기 어렵다. 결과적으로 전자 문서와 메시지에 대한 법적 효력, 감사 가능성, 부인 방지를 보장하는 실질적 수단으로 기능한다.

 

2. CMS SignedData 이해 - 정의 및 기본 구성 요소

SignedData는 CMS(Cryptographic Message Syntax)의 컨텐츠 유형 중 하나로, 전자 서명을 포함한 데이터를 구조화하여 전달하기 위한 표준 형식이다. 이 구조는 메시지의 무결성과 서명자의 신원을 검증할 수 있는 모든 정보를 하나의 데이터 묶음으로 포장하는 역할을 한다. 보다 구체적으로, SignedData는 다음과 같은 구성 요소를 포함할 수 있다.

 

  • 전자 서명 대상이 되는 원본 메시지
  • 메시지 해시 처리에 사용된 해시 알고리즘 정보
  • 서명자가 실제로 생성한 서명값
  • 서명자가 사용한 공개키를 포함하는 인증서
  • 폐지된 인증서 정보를 담은 CRL

 

이러한 정보들을 모두 포함한 SignedData는 수신자에게 전달되어, 해당 메시지가 위변조되지 않았고 서명자가 신뢰할 수 있는 인물임을 검증할 수 있게 한다.

 

전자 서명은 기본적으로 다음과 같은 절차를 따른다.

 

  • 1️⃣ 원본 메시지를 해시 함수를 이용해 해시 처리한다.
  • 2️⃣ 생성된 해시값에 대해 서명자의 개인 키로 서명하여 전자 서명값을 만든다.

 

이러한 서명 절차는 이론적으로 간단하지만, 실제 응용에서는 다양한 검증 요소가 필요하기 때문에 SignedData를 전송한다. 그 이유는 다음과 같다.

 

  • 서명값만으로는 검증에 필요한 정보가 부족하다.
수신자는 서명값만으로는 어떤 해시 알고리즘과 서명 알고리즘이 사용되었는지 알 수 없다. 또한 서명 검증을 위해 서명자의 공개키가 필요하지만, 별도로 제공되지 않으면 검증이 불가하다.

 

  • 서명 구조 자체가 무결성을 보장하는 역할을 한다.
아래 챕터에서 자세히 설명할테지만 SignedData는 서명값뿐 아니라, 관련 부가 정보를 구조화된 형식으로 포함하여 함께 전달한다. 이는 누구나 SignedData를 사용하는 형식이 체계화되어 있다는 의미로 구조화된 SignedData가 깨졌다는 것은 송수신 간의 데이터 신뢰성을 보장할 수 없음을 의미한다.

 

결과적으로, 단순한 서명값만으로는 서명 검증에 필요한 정보가 충분하지 않다. 해시 알고리즘, 서명 알고리즘, 공개키 정보 등 다양한 요소들이 함께 제공되어야 비로소 서명의 유효성을 확인할 수 있다. 따라서 SignedData는 이러한 정보를 하나의 통합 구조로 안전하게 전달하기 위해 설계된 형식이라고 말할 수 있다.

 

< 편지를 받는 것만으로는 충분하지 않다. 그 진위를 확인할 때 신뢰가 완성된다. - 요하네스 페르메이르, 편지를 읽는 여인 >

 

3. SignedData의 보안 기능 - 메시지 무결성과 부인 방지

SignedData는 다음 두 가지 핵심 목적을 충족하기 위해 설계되었다.

 

  • ✅ 메시지 무결성(Integrity) 보장
SignedData는 전송된 메시지가 전송 중 변조되지 않았음을 검증할 수 있도록 한다. 이는 단순히 해시값을 비교하는 수준을 넘어서, 서명자가 생성한 서명값을 통해 보장된다.

서명자는 메시지를 해시한 후, 그 해시값에 대해 자신의 개인키로 서명을 생성한다. 수신자는 동일한 해시 알고리즘으로 메시지를 해시하고, 서명값을 검증함으로써 메시지가 변경되지 않았음을 판단할 수 있다. 만약 메시지가 변조되었거나, 서명자가 일치하지 않으면 해시값이 달라지고, 검증에 실패하게 된다.

즉, SignedData는 서명된 메시지에 대한 변조 방지 장치 역할을 한다.

 

  • ✅ 부인 방지(Non-repudiation) 제공
SignedData는 서명자가 나중에 해당 메시지를 "내가 서명한 것이 아니다."라고 부인하는 것을 방지할 수 있다. 이는 전자 서명 검증 과정에서 서명자의 인증서를 활용함으로써 가능해진다.

SignedData에는 서명자의 X.509 인증서가 포함될 수 있으며, 이 인증서에는 서명자의 공개 키가 포함되어 있다. 수신자는 해당 공개키를 사용하여 서명을 검증함으로써, 특정 서명자가 서명했다는 사실을 확인할 수 있다. 이때 개인키가 안전하게 관리되고 있다는 전제가 충족된다면, 그 서명은 오직 해당 서명자만이 생성할 수 있었던 것으로 간주된다.

즉, SignedData는 서명자가 해당 서명을 부인하기 어렵도록 법적 책임성을 강화하는 역할을 한다.

 

요약하면, SignedData는 PKI 기반 신뢰 체계의 핵심 구성 요소이며, 전자 문서, 인증 토큰 등 다양한 환경에서 신뢰 가능한 메시지 전달의 기반으로 활용된다.

 

4. SignedData 구조에 대한 설명

SignedData는 RFC 5652에서 정의된 구조체로, 전자 서명과 관련된 정보를 하나의 데이터 단위로 포장하여 전송할 수 있도록 설계되어 있다. SignedData의 ASN.1 기본 구조는 다음과 같다.

SignedData ::= SEQUENCE {
    version CMSVersion,
    digestAlgorithms DigestAlgorithmIdentifiers,
    encapContentInfo EncapsulatedContentInfo,
    certificates [0] IMPLICIT CertificateSet OPTIONAL,
    crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
    signerInfos SignerInfos
}
  • version : CMS 버전을 명시하는 필드로 포함된 필드 및 사용 방식에 따라 달라진다.
  • digestAlgorithms : 서명에 사용된 해시 알고리즘들의 목록으로, 각 SignerInfo.digestAlgorithm과 일치해야 한다.
  • encapContentInfo : 서명 대상이 되는 실제 컨텐츠 정보를 명시하는 필드이다. 내부 구조는 아래와 같다.
    • eContentType : 메시지의 유형을 명시
    • eContent : 원본 메시지 자체를 포함하는 필드. 대용량 데이터일 경우 해당 항목을 비워둘 수도 있다.
EncapsulatedContentInfo ::= SEQUENCE {
    eContentType ContentType,
    eContent [0] EXPLICIT OCTET STRING OPTIONAL
}

 

  • certificates : 선택 사항으로 서명자의 인증서 + 연관된 인증서 목록을 담을 수 있다. 수신자는 이 항목을 통해 공개키를 확보한다. 네트워크 접근이 불가능한 환경에서 이 필드에 인증서를 포함하는 것이 일반적이다.
  • crls : 선택 사항으로 인증서 폐지 목록(CRL)을 담을 수 있다. 수신자는 이 항목을 통해 인증서의 만료 혹은 폐지를 검증한다. OCSP를 사용하지 못하는 환경에서 이 필드에 CRL를 포함하는 것이 일반적이다.
  • signerInfos : 전자 서명 정보를 리스트 형태로 담고 있다. SignedData 구조의 핵심으로 각 서명자의 검증에 필요한 정보를 아래와 같이 포함한다.
    • sid : 서명자를 식별하는 정보
    • digestAlgorithm : 서명자가 메시지를 해시할 때 사용된 알고리즘을 명시하여, 검증 측에서도 동일한 알고리즘을 사용할 수 있게 한다.
    • signedAttrs : 선택 사항으로 컨텐츠 유형, 서명 시각, 메시지 다이제스트 등 서명에 포함되는 속성을 명시한다.
    • signatureAlgorithm : 서명자가 전자 서명을 생성할 때 사용된 알고리즘을 명시하여, 검증 측에서도 동일한 알고리즘을 사용할 수 있게 한다.
    • signature : 서명자가 생성한 서명값
    • unsignedAttrs : 선택 사항으로 서명 후에 추가될 수 있는 속성들을 명시한다.
SignerInfo ::= SEQUENCE {
    version CMSVersion,
    sid SignerIdentifier,
    digestAlgorithm DigestAlgorithmIdentifier,
    signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
    signatureAlgorithm SignatureAlgorithmIdentifier,
    signature SignatureValue,
    unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL
}

 

SignedData는 이러한 정보들을 모두 포함함으로써, 하나의 독립적인 검증 단위를 형성한다. 이 구조를 통해 수신자는 별도 외부 참조 없이 서명 유효성을 판단할 수 있으며, 독립적이고 재현 가능한 검증이 가능해진다.

 

5. SignedData 생성 및 검증 절차

SignedData의 이해를 돕기 위해 실제 전자 서명 시스템에서 사용되는 두 단계의 흐름에 대해 설명한다.

 

5-1. 서명 생성 시점

서명자는 메시지의 진위성과 무결성을 보장하기 위해 다음 절차를 통해 SignedData를 구성한다.

 

  • 1️⃣ 단계 : 컨텐츠 준비
전자 서명 대상이 되는 원본 메시지를 준비한다. 해당 메시지는 encapContentInfo > eContent 필드에 포함될 수 있으며, 대용량 메시지의 경우 이 필드를 비워두고 외부에 별도로 전달할 수도 있다.

 

  • 2️⃣ 단계 : 해시 계산
서명자가 선택한 해시 알고리즘(SHA-256 등)을 사용하여 원본 메시지에 대한 해시값을 계산한다. 사용된 해시 알고리즘은 다음 위치에 명시된다.

* SignedData > DigestAlgorithm : 전체 구조 수준
* SignedData > SignerInfo > DigestAlgorithm : 개별 서명자 수준

 

  • 3️⃣ 단계 : SignedAttributes 작성 (선택 사항)
서명자가 SignedData > SignerInfo > SignedAttrs 필드를 사용하는 경우, 다음과 같은 속성들을 포함한다.

* 메시지 다이제스트
* 컨텐츠 타입
* 서명 시각 등

 

  • 4️⃣ 단계 : 전자 서명 생성
서명자는 자신의 개인 키를 사용하여 다음 중 하나를 서명한다.

* SignedAttrs가 존재하는 경우 : 구조화된 SignedAttributes 전체를 DER로 인코딩한 값에 대해 서명
* SignedAttrs가 존재하지 않는 경우 : 2️⃣ 단계에 계산한 해시값 대해 서명

이후 서명값은 SignedData > SignerInfo > Signature에 저장된다.

 

  • 5️⃣ 단계 : 인증서 및 기타 부가정보 포함 (선택 사항)
서명자는 수신자의 상태 혹은 요구에 따라 인증서 목록 및 인증서 폐지 목록을 포함시킬 수 있다.

예시: 수신자가 네트워크에 접근하지 못하는 환경이라면, 인증서를 SignedData 구조 내에 포함시키는 것이 바람직하다.

 

  • 6️⃣ 단계 : 전체 SignedData 구조 조립 및 전송
서명자는 위의 모든 정보를 조합하여 최종 SignedData 구조를 완성하고, 이를 수신자에게 전달한다.

 

5-2. 서명 검증 시점

수신자는 전달받은 SignedData를 기반으로 다음의 절차에 따라 서명과 메시지의 유효성을 검증한다.

 

  • 1️⃣ 단계 : SignedData 파싱
수신자는 서명자로부터 전달받은 SignedData를 파싱하여 다음의 정보를 추출한다.

* 원본 메시지 또는 해시값
* 서명값
* 해시 알고리즘 및 서명 알고리즘
* 부가 정보

 

  • 2️⃣ 단계 : 공개키 확보
수신자는 서명자의 공개키를 다음 경로 중 하나를 통해 획득한다.

* 외부의 인증서 저장소 : 웹서버 혹은 LDAP 등
* SignedData > Certificates 필드

 

  • 3️⃣ 단계 : 인증서 검증
수신자는 신뢰 체계에 다라 인증서의 유효성을 검증한다.

* 인증 기관(CA, Certificate Authority)의 신뢰성
* 인증서의 유효 기간 만료 여부
* 인증서 폐지 여부 (CRL, OCSP 활용)

 

  • 4️⃣ 단계 : 검증 대상 값 도출
SignedData > SignerInfo > SignedAttrs의 존재 여부에 따라 검증 대상 값 도출이 달라진다.

* SignedAttrs가 존재하는 경우 
  ① 복호화된 서명값과 signedAttrs 전체를 DER 인코딩한 후 해시 처리한 값을 도출하여 일치하는지 비교한다.
  ② 메시지 해시값과 signedAttrs.message-digest 값을 도출하여 일치하는지 비교한다.

* signedAttrs가 존재하지 않는 경우: 복호화된 서명값과 메시지 해시값을 도출하여 일치하는지 비교한다.

 

  • 5️⃣ 단계 : 최종 판정
다음 조건이 모두 충족되면 SignedData는 신뢰할 수 있는 것으로 간주된다.

* 3️⃣ 단계의 서명자 인증서의 유효성 검증에 대한 성공
* 서명자와 수신자가 사용한 서명 알고리즘 및 해시 알고리즘의 일치
* 4️⃣ 단계에서 도출한 검증 대상 값들을 비교 시 일치

이 과정을 통해 수신자는 메시지의 무결성, 서명자의 정당성, 그리고 서명에 대한 부인 방지 조건을 모두 충족시킬 수 있다.

 

6. SignedData 실무 활용 예시 - signedAttrs 유무에 따른 서명 방식

앞서 설명한 SignedData의 생성 및 검증 과정을 보다 직관적으로 이해할 수 있도록 5. SignedData 생성 및 검증 절차를 도식화하여 설명한다. 각 방식은 SignerInfo > signedAttrs 필드의 존재 여부에 따라 서명 생성 및 검증 방식이 다르게 동작하므로 유의한다. 

 

6-1. 일반적인 SignedData 서명 방식 - SignedAttrs 미포함

이 방식은 signedAttrs 필드를 사용하지 않고, 원본 메시지의 해시값만을 서명 대상으로 하는 전형적인 방식이다.

< SignedData 활용 예시 : 전형적인 방식 >

 

  • 1️⃣ 서명자는 수신자에게 전송할 메시지(원문)를 준비한다.
  • 2️⃣ 서명자는 메시지를 지정된 해시 알고리즘으로 해시하여 해시값을 생성한다.
  • 3️⃣ 서명자는 생성된 해시값을 자신의 개인 키로 서명하여 서명값을 생성한다.
  • 4️⃣ 서명자는 아래 필드를 포함하여 SignedData 구조를 구성한다.
    • encapContentInfo.eContent : 메시지(원문)
    • certificates : (필요 시) 서명자의 인증서
    • crls : (필요 시) 인증서 폐지 목록
    • signerInfo.digestAlgorithm : 사용된 해시 알고리즘
    • signerInfo.signatureAlgorithm : 사용된 서명 알고리즘
    • signerInfo.signature : 생성된 서명값
  • 5️⃣ 서명자는 SignedData 전체를 수신자에게 전송한다.
  • 6️⃣ 수신자는 SignedData를 파싱하여 메시지(원문)와 서명값 등을 추출한다.
  • 7️⃣ 수신자는 인증서의 유효성을 CRL or OCSP를 통해 검증한다.
  • 8️⃣ 수신자는 서명값을 서명자의 공개 키로 복호화하여 해시값 A을 획득한다.
  • 9️⃣ 수신자는 메시지(원문)를 동일한 해시 알고리즘으로 해시하여 해시값 B를 생성한다.
  • 🔟 수신자는 해시값 A해시값 B가 일치하는지 비교한다.
    • A = B, 메시지가 변경되지 않았고 실제 서명자가 보낸 것임을 확인한다.
    • AB, 메시지가 변조되었거나 실제 서명자가 보낸 것이 아님을 확인한다.

 

6-2. SignedAttrs를 활용한 SignedData 서명 강화 

이 방식은 signedAttrs 필드를 활용하여 메시지 외에 추가 정보도 함께 보호하는 방식이다. 부인 방지 요구사항이 있는 환경에서 사용된다.

< SignedData 활용 예시 : signedAttrs 필드를 활용하여 보안 요건을 강화해야 할 때 사용하는 방식 >

 

 

  • 1️⃣ 서명자는 수신자에게 전송할 메시지(원문)를 지정된 해시 알고리즘으로 해시하여 해시값을 생성한다.
  • 2️⃣ 서명자는 signedAttrs 구성 → DER로 인코딩하여 바이트 값 획득 → 지정된 해시 알고리즘으로 해시 → 자신의 개인키로 서명하여 서명값을 생성한다.
  • 3️⃣ 서명자는 아래 필드를 포함하여 SignedData 구조를 구성한다.
    • encapContentInfo.eContent : 메시지(원문)
    • certificates : (필요 시) 서명자의 인증서
    • crls : (필요 시) 인증서 폐지 목록
    • signerInfo.digestAlgorithm : 사용된 해시 알고리즘
    • signerInfo.signatureAlgorithm : 사용된 서명 알고리즘
    • signerInfo.signature : 2️⃣ 과정에서 생성한 서명값
    • signerInfo.signedAttrs.message-digest : 1️⃣ 과정에서 생성한 해시값
  • 4️⃣ 서명자는 SignedData 전체를 수신자에게 전송한다.
  • 5️⃣ 수신자는 SignedData를 파싱하여 메시지(원문)와 서명값 등을 추출한다. 이후 유효성 검증을 수행한다.(CRL or OCSP)
  • 6️⃣ 수신자는 서명값을 서명자의 공개 키로 복호화하여 해시값 A을 획득한다.
  • 7️⃣ 수신자는 signedAttrs를 DER로 인코딩하고 해시하여 해시값 B를 생성한다.
  • 8️⃣ 수신자는 메시지(원문)를 동일한 해시 알고리즘으로 해시하여 해시값 C을 생성한다.
  • 9️⃣ 수신자는 signedAttrs.message-digest의 해시값 D를 추출한다.
  • 🔟 수신자는 도출한 값을 기반으로 비교한다.
    • A = B 여부는 signedAttrs 전체가 서명자에 의해 생성되었는지를 확인하는 과정으로, 서명 시간, 컨텐츠 유형 등 메타 정보까지 포함된 전자 서명의 무결성과 부인 방지를 평가한다.
    • C = D 여부는 메시지(원문) 자체의 무결성 보장을 위한 검증이며, 전송 중의 위변조 여부를 판단한다.

 

결론적으로, signedAttrs를 활용하면 메시지 본문뿐 아니라 메타 정보까지 서명 대상에 포함된다. 이를 통해 리플레이 공격을 방지하고, 서명 시각과 컨텐츠 유형과 같은 속성의 정합성을 함께 검증할 수 있다. 검증 항목은 다소 증가하지만, 그만큼 서명이 특정 시점에 특정 메시지를 대상으로 생성된 것임이 명확해지므로, 법적 효력 확보나 높은 수준의 보안 요건이 요구되는 환경에서 signedAttrs 필드는 필수적으로 활용된다.

 

< 나무의 나이테처럼, signedAttrs는 시간과 정황을 함께 증명하는 디지털 메타데이터의 증거이다. >

 

7. 마무리 - PKI 신뢰 체계에서의 SignedData 중요성과 의미

이번 글을 정리하면 다음과 같다.

 

CMS의 SignedData는 단순한 전자 서명 포맷을 넘어서, 디지털 환경에서 신뢰를 실현하는 구조적 기반이다. 전자 서명의 유효성을 판단하기 위해 필요한 정보를 하나의 구조 내에 안전하게 포장하여 전달함으로써, 송수신자 간 신뢰 검증이 독립적이고 재현 가능하게 이루어질 수 있도록 한다.

 

이 구조는 메시지의 무결성, 서명자의 신원 확인, 그리고 부인 방지라는 보안의 핵심 목표를 구현하는 데 중추적인 역할을 수행한다. 특히 signedAttrs와 같은 확장 필드를 통해 서명 시점, 컨텐츠 유형 등 문맥 정보를 보호할 수 있다는 점에서, SignedData는 단순 서명을 넘어서 문서의 법적 효력과 행위의 책임성을 담보하는 신뢰 구조로 기능한다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유