ASN.1
ASN.1 이란
ASN.1(Abstract Syntax Notation One)은 데이터 구조를 플랫폼과 언어에 독립적으로 정의하기 위한 표준 표기법이다. 1984년 CCITT(현 ITU-T)에서 처음 표준화되었고, 현재는 ITU-T X.680 시리즈(ISO/IEC 8824)로 관리되고 있다.
쉽게 말하면, 서로 다른 시스템(언어, OS, CPU 아키텍처가 다른)끼리 데이터를 주고받을 때 “이 메시지는 이런 필드들로 구성되어 있다”를 약속하는 명세 언어이다. 요즘 많이 쓰는 Protocol Buffers의 .proto 파일이나 JSON Schema와 비슷한 역할을 한다고 보면 이해하기 쉽다. (사실 ASN.1이 훨씬 오래된 선배다.)
1
2
3
4
5
6
7
8
9
10
VehicleExample DEFINITIONS AUTOMATIC TAGS ::= BEGIN
VehicleInfo ::= SEQUENCE {
id INTEGER (0..255),
speed INTEGER (0..8191),
lightsOn BOOLEAN,
plate IA5String (SIZE(1..8)) OPTIONAL
}
END
위처럼 ASN.1로 메시지 구조를 정의하면, ASN.1 컴파일러(asn1c, OSS, Objective Systems 등)를 통해 C/C++/Java 등의 코드(구조체 + 인코더/디코더)를 자동으로 생성할 수 있다.
V2X에서 ASN.1을 알아야 하는 이유
V2X(Vehicle-to-Everything) 통신에서 오가는 거의 모든 메시지가 ASN.1로 정의되어 있기 때문이다.
- SAE J2735 : BSM(Basic Safety Message), SPaT, MAP 등 미국 V2X 메시지 셋 → ASN.1 정의, UPER 인코딩
- ETSI ITS : CAM, DENM 등 유럽 V2X 메시지 → ASN.1 정의, UPER 인코딩
- IEEE 1609.2 / 1609.2.1 : V2X 보안 메시지, 인증서, 인증서 관리 메시지 → ASN.1 정의, COER 인코딩
- 3GPP LTE-V2X / NR-V2X : 하위 무선 계층의 RRC 메시지도 ASN.1로 정의
즉, V2X 메시지를 만들거나 파싱하거나, 서명을 검증하거나, 인증서를 발급받는 모든 과정에서 ASN.1 명세를 읽고 인코딩된 바이트를 해석할 수 있어야 한다. 특히 보안 쪽은 인코딩된 바이트 그대로 서명/검증을 하기 때문에, 바이트 한 개만 달라져도 서명 검증이 실패한다. 디버깅을 하려면 ASN.1과 인코딩 규칙을 이해하는 것이 필수다.
특징
1. 추상 구문(Abstract Syntax)과 전송 구문(Transfer Syntax)의 분리
ASN.1의 가장 큰 특징이다. ASN.1은 “어떤 데이터인가”만 정의하고, “어떻게 바이트로 표현할 것인가”는 별도의 인코딩 규칙(Encoding Rules) 이 담당한다. 그래서 같은 ASN.1 정의를 상황에 따라 BER, DER, PER, OER, XER, JER 등 다양한 형식으로 인코딩할 수 있다.
2. 풍부한 타입 시스템
| 분류 | 타입 | 설명 |
|---|---|---|
| 기본 타입 | BOOLEAN, INTEGER, ENUMERATED, NULL | 불리언, 정수, 열거형, 값 없음 |
| 바이트/비트 | OCTET STRING, BIT STRING | 바이트 배열, 비트 배열 |
| 문자열 | IA5String, UTF8String, PrintableString 등 | 문자 집합별 문자열 |
| 식별자 | OBJECT IDENTIFIER | 전역 고유 식별자 (예: 1.2.840.10045.2.1) |
| 구조 타입 | SEQUENCE, SEQUENCE OF, SET, CHOICE | 구조체, 배열, 순서 없는 집합, 공용체(union) |
3. 제약 조건(Constraints)
INTEGER (0..255), OCTET STRING (SIZE(32))처럼 값의 범위나 크기를 명세 수준에서 제한할 수 있다. 제약 조건은 단순한 유효성 검사뿐 아니라 인코딩 크기에도 영향을 준다. 예를 들어 PER은 INTEGER (0..255)를 딱 8비트로 인코딩한다.
4. 선택 필드와 기본값
OPTIONAL, DEFAULT 키워드로 생략 가능한 필드를 정의할 수 있다.
5. 확장성(Extensibility)
... (확장 마커)를 넣어두면, 나중에 필드나 선택지를 추가해도 이전 버전의 디코더가 새 메시지를 깨지지 않고 디코딩할 수 있다. 표준이 계속 개정되는 V2X 환경에서 매우 중요한 기능이다.
1
2
3
4
5
SomeMessage ::= SEQUENCE {
fieldA INTEGER,
..., -- 확장 마커
fieldB BOOLEAN -- 이후 버전에서 추가된 필드
}
6. 모듈화
DEFINITIONS ... BEGIN ... END로 모듈을 정의하고, IMPORTS로 다른 모듈의 타입을 가져와 재사용할 수 있다.
활용 분야
ASN.1은 눈에 잘 띄지 않지만, 생각보다 우리 주변 곳곳에서 사용되고 있다.
| 분야 | 사용처 | 인코딩 |
|---|---|---|
| 보안/PKI | X.509 인증서, PKCS 시리즈, CMS, OCSP | DER, BER |
| 네트워크 관리 | SNMP | BER |
| 디렉토리 | LDAP | BER |
| 인증 | Kerberos | DER |
| 이동통신 | 3GPP LTE/5G RRC, S1AP, NGAP 등 | UPER, APER |
| 항공 | ATN, CPDLC | PER |
| 금융/카드 | EMV 일부 | BER |
| V2X | SAE J2735, ETSI ITS, IEEE 1609.2 | UPER, COER |
우리가 매일 쓰는 HTTPS의 TLS 인증서(X.509)도 ASN.1로 정의되고 DER로 인코딩된 데이터다. .pem 파일의 Base64를 디코딩하면 나오는 바이너리가 바로 DER이다.
인코딩
앞서 말했듯이 ASN.1은 구조만 정의하고, 실제 바이트 표현은 인코딩 규칙이 결정한다. 대표적인 인코딩 규칙은 다음과 같다.
| 인코딩 규칙 | 표준 | 특징 |
|---|---|---|
| BER (Basic Encoding Rules) | X.690 | TLV(Tag-Length-Value) 구조, 같은 값을 여러 방식으로 인코딩 가능 |
| DER (Distinguished Encoding Rules) | X.690 | BER의 부분집합, 인코딩 결과가 유일(canonical) → 서명에 적합 |
| CER (Canonical Encoding Rules) | X.690 | BER의 canonical 부분집합, 대용량 스트리밍용 |
| PER (Packed Encoding Rules) | X.691 | 제약 조건을 활용해 비트 단위로 압축, Aligned(APER) / Unaligned(UPER) |
| OER (Octet Encoding Rules) | X.696 | 바이트(octet) 단위 정렬, PER보다 크지만 인코딩/디코딩이 빠름, Canonical OER(COER) 존재 |
| XER (XML Encoding Rules) | X.693 | XML 형식 |
| JER (JSON Encoding Rules) | X.697 | JSON 형식 |
인코딩 비교 예시
아래 타입을 인코딩한다고 해보자.
1
2
3
4
5
Example ::= SEQUENCE {
id INTEGER (0..255),
lightsOn BOOLEAN
}
-- 값: { id 10, lightsOn TRUE }
DER (AUTOMATIC TAGS 기준)
1
2
3
30 06 -- SEQUENCE, 길이 6
80 01 0A -- [0] id, 길이 1, 값 10
81 01 FF -- [1] lightsOn, 길이 1, 값 TRUE
→ 총 8 바이트. 각 필드마다 Tag와 Length가 붙기 때문에 크기가 크지만, 스키마 없이도 구조를 어느 정도 파악할 수 있다.
UPER
1
2
3
id : 0..255 범위 → 8비트 → 00001010
lightsOn : 1비트 → 1
합계 9비트 → 0으로 패딩 → 00001010 10000000
→ 0A 80, 총 2 바이트. Tag/Length 없이 제약 조건만으로 필요한 비트 수를 계산해서 꽉 채운다. 대역폭이 귀한 무선 환경(V2X 메시지, 이동통신)에서 주로 사용한다.
OER
1
2
id : 0..255 범위 → 1 바이트 → 0A
lightsOn : 1 바이트 → FF
→ 0A FF, 총 2 바이트. 바이트 단위로 정렬되어 있어 비트 연산이 거의 필요 없기 때문에, PER보다 인코딩/디코딩 속도가 빠르다.
이 예시에서는 UPER와 OER의 크기가 같지만, 필드가 많아지고 범위가 애매한 값(예: 0..8191 → UPER 13비트, OER 2바이트)이 많아질수록 UPER가 더 작아진다.
왜 V2X 보안은 COER를 쓸까?
IEEE 1609.2는 COER(Canonical OER) 를 사용한다. 이유는 다음과 같다.
- Canonical(유일성) : 서명은 인코딩된 바이트에 대해 계산된다. 같은 값이 여러 방식으로 인코딩될 수 있다면, 송신 측과 수신 측이 서로 다른 바이트로 서명을 검증하게 되어 실패할 수 있다. 따라서 하나의 값은 반드시 하나의 바이트열로만 표현되어야 한다.
- 속도 : 차량은 초당 수백~수천 개의 메시지를 수신하고 서명을 검증해야 한다. 바이트 정렬된 OER은 하드웨어/소프트웨어에서 빠르게 처리할 수 있다.
- 적당한 크기 : BER/DER처럼 모든 필드에 TLV를 붙이지 않아 크기도 충분히 작다.
정리하면, V2X에서는 애플리케이션 메시지(BSM, CAM 등)는 UPER, 보안 계층(서명, 인증서)은 COER를 사용한다.
IEEE 1609.2
IEEE 1609.2는 WAVE(Wireless Access in Vehicular Environments) 환경의 보안 서비스를 정의하는 표준이다. V2X 메시지에 대한 서명/암호화 형식과, 이를 위한 인증서 형식을 ASN.1로 정의하고 있다.
왜 X.509를 쓰지 않을까?
일반적인 인터넷 환경에서는 X.509 인증서를 사용하지만, V2X에서는 다음과 같은 제약이 있다.
- 메시지가 초당 10회 정도 브로드캐스트되며, 무선 대역폭이 제한적이다.
- X.509 인증서는 수백 바이트 ~ 1KB 이상으로 크다.
- 수신 측은 매우 짧은 시간 안에 서명을 검증해야 한다.
그래서 1609.2는 훨씬 작고 단순한 자체 인증서 형식을 정의하고, 타원곡선 암호(ECDSA, ECIES)와 암묵적 인증서(Implicit Certificate, ECQV) 를 지원해 크기를 더 줄였다.
주요 구조
1609.2 보안 메시지의 최상위 구조는 Ieee1609Dot2Data이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Ieee1609Dot2Data ::= SEQUENCE {
protocolVersion Uint8(3),
content Ieee1609Dot2Content
}
Ieee1609Dot2Content ::= CHOICE {
unsecuredData Opaque,
signedData SignedData,
encryptedData EncryptedData,
signedCertificateRequest Opaque,
...
}
SignedData ::= SEQUENCE {
hashId HashAlgorithm,
tbsData ToBeSignedData,
signer SignerIdentifier,
signature Signature
}
SignerIdentifier ::= CHOICE {
digest HashedId8,
certificate SequenceOfCertificate,
self NULL,
...
}
- unsecuredData : 보안이 적용되지 않은 원본 데이터
- signedData : 서명된 데이터.
tbsData에 실제 페이로드와 헤더(PSID, 생성 시간 등)가 들어간다. - encryptedData : 암호화된 데이터
- signer : 서명자 식별 방법
certificate: 인증서 전체를 함께 전송digest: 인증서의 해시 8바이트(HashedId8)만 전송 → 수신 측이 이미 해당 인증서를 캐싱하고 있을 때 사용해 크기를 줄인다.
여기서 PSID(Provider Service Identifier) 는 메시지가 어떤 서비스(애플리케이션)에 속하는지를 나타내는 값으로, 인증서에 포함된 권한(appPermissions)과 비교해 해당 인증서가 이 메시지를 서명할 권한이 있는지 검증하는 데 사용된다.
인증서 구조
1
2
3
4
5
6
7
CertificateBase ::= SEQUENCE {
version Uint8(3),
type CertificateType, -- explicit / implicit
issuer IssuerIdentifier,
toBeSigned ToBeSignedCertificate,
signature Signature OPTIONAL
}
- Explicit 인증서 : 공개키와 발급자의 서명이 인증서에 명시적으로 포함된다.
- Implicit 인증서 : 서명 필드가 없고, 공개키 재구성 값(reconstruction value)만 포함된다. 수신 측은 발급자의 공개키와 재구성 값을 이용해 인증서 소유자의 공개키를 계산한다. 서명 필드가 없으므로 크기가 더 작다.
ToBeSignedCertificate에는 인증서 ID, 유효 기간, 유효 지역(region), 권한(PSID와 SSP) 등이 들어간다.
IEEE 1609.2.1
IEEE 1609.2가 “인증서와 보안 메시지가 어떻게 생겼는가”를 정의한다면, IEEE 1609.2.1은 “차량(End Entity)이 그 인증서를 어떻게 발급받고 관리하는가” 를 정의하는 표준이다. 즉, 차량/노변장치(RSU) 같은 End Entity(EE) 와 SCMS(Security Credential Management System) 사이의 인증서 관리 인터페이스를 다룬다.
SCMS 주요 구성 요소
| 구성 요소 | 역할 |
|---|---|
| Root CA | 신뢰의 최상위 기관 |
| ECA (Enrollment CA) | 등록 인증서(Enrollment Certificate) 발급 |
| RA (Registration Authority) | EE의 요청을 받아 ACA로 전달, 응답을 EE에게 전달 |
| ACA (Authorization CA) | 실제 메시지 서명에 사용하는 인가 인증서(Authorization / Pseudonym Certificate) 발급 |
| MA (Misbehavior Authority) | 오작동/악의적인 EE 탐지 및 인증서 폐기 |
인증서 발급 흐름
- 등록(Enrollment) : 차량이 생산/초기화 단계에서 ECA로부터 등록 인증서를 발급받는다. 이 인증서는 “이 장치는 신뢰할 수 있는 장치다”를 증명하며, 이후 RA에 요청할 때 자신을 인증하는 데 사용된다.
- 인가 인증서 요청 : 차량은 등록 인증서로 서명한 요청을 RA에 보내 인가 인증서(가명 인증서) 를 요청한다.
- 다운로드 : RA/ACA가 생성한 인증서 묶음을 차량이 다운로드한다.
- CRL 및 기타 파일 다운로드 : 인증서 폐기 목록(CRL), 인증서 체인 등 신뢰 관련 파일을 다운로드한다.
프라이버시와 Butterfly Key Expansion
V2X 메시지는 브로드캐스트되기 때문에, 하나의 인증서만 계속 사용하면 차량의 이동 경로가 추적될 수 있다. 그래서 차량은 수십 개의 가명 인증서(Pseudonym Certificate) 를 주기적으로 바꿔가며 사용한다.
이때 수많은 인증서를 효율적으로 요청하기 위해 Butterfly Key Expansion 기법을 사용한다. 차량은 시드 키(caterpillar key)와 확장 함수만 RA에 한 번 전달하고, RA는 이를 확장해 여러 개의 공개키를 만들어 ACA에 인증서 발급을 요청한다. 이 과정에서 RA는 어떤 인증서가 어떤 차량 것인지 알 수 있지만 인증서 내용을 볼 수 없고, ACA는 인증서 내용을 알지만 어떤 차량의 요청인지 모르도록 설계되어 있다. (권한 분리를 통한 프라이버시 보호)
ASN.1 관점에서의 1609.2.1
1609.2.1의 모든 메시지 역시 ASN.1로 정의되고 COER로 인코딩된다. 최상위에는 ScmsPdu라는 공통 컨테이너가 있고, 그 안에 인터페이스별 메시지가 CHOICE로 들어간다.
1
2
3
4
5
6
7
8
ScmsPdu ::= SEQUENCE {
version Uint8(2),
content CHOICE {
eca-ee EcaEeInterfacePdu, -- EE ↔ ECA (등록 인증서)
ee-ra EeRaInterfacePdu, -- EE ↔ RA (인가 인증서 요청/다운로드)
...
}
}
그리고 이 ScmsPdu는 다시 IEEE 1609.2의 SignedData / EncryptedData로 감싸져서 전송된다. 즉, 1609.2.1 메시지 = 1609.2 보안 컨테이너 안에 담긴 ASN.1 메시지라는 구조이기 때문에, 두 표준과 ASN.1 인코딩(COER)을 함께 이해하고 있어야 SCMS와의 통신을 구현하거나 디버깅할 수 있다.
정리
- ASN.1은 플랫폼 독립적으로 데이터 구조를 정의하는 표기법이고, 실제 바이트 표현은 인코딩 규칙(BER/DER/PER/OER 등)이 결정한다.
- V2X에서는 애플리케이션 메시지는 UPER, 보안 메시지와 인증서는 COER로 인코딩한다.
- IEEE 1609.2는 V2X 보안 메시지와 인증서 형식을, IEEE 1609.2.1은 SCMS와의 인증서 관리 프로토콜을 ASN.1로 정의한다.