CVC example
Example CVC from BSI TR-03110 (Public/Test Patterns)
BSI TR-03110 defines the CVC format precisely for use in ePassports (EAC) and eID systems, but BSI does not publish raw CVC test files directly on their site (they focus on test CSCA X.509 roots and interoperability guidelines). Instead, public test CVCs are widely available in open-source implementations and interoperability test suites that follow TR-03110 exactly.
These test certificates typically use the fictional country code "UT" (Utopia) or "DETEST" to avoid conflicting with production keys. They form short chains: CVCA → DV → Terminal (IS).
Libraries like EJBCA (with CVC plugin), OpenPACE, pycvc (Python), and JMRTD (Java) generate or include compliant test CVCs for developers.
Typical Parsed Test CVCA Example (Self-Signed Root)
Here is a realistic parsed example of a test CVCA CVC (based on common public test patterns from open-source TR-03110 implementations):
- Profile Identifier (tag 0x5F29):
00(initial profile) - Certificate Authority Reference (CAR) (tag 0x42):
UTCVCA00001(ASCII, identifies the issuer) - Public Key (tag 0x7F49): Nested TLV
- OID: id-TA-ECDSA-SHA-256 (1.0.36.3.3.2.8.1.1.7 for brainpoolP256r1)
- Domain parameters: BrainpoolP256r1 reference
- Uncompressed point: 04 || X (32 bytes) || Y (32 bytes) – e.g., a test key point like
04 6B17D1F2...(full hex varies by generated key) - Certificate Holder Reference (CHR) (tag 0x5F20):
UTCVCA00001(same as CAR for root/self-signed) - Effective Date (tag 0x5F25):
100101(YYMMDD → 2010-01-01) - Expiration Date (tag 0x5F24):
251231(2025-12-31) - Signature (tag 0x5F37): ECDSA-SHA-256 signature (r || s, ~64 bytes)
No CHAT (0x7F4C) or extensions (0x65) in a basic CVCA.
The signed body is the raw concatenation of all TLV objects from 0x5F29 to 0x5F24. This keeps parsing simple on cards (no full ASN.1 parser needed).
Quick Comparison to X.509 (Connecting Dots)
| Field/Aspect | X.509 (ASN.1 DER) | CVC (BER-TLV) |
|---|---|---|
| Issuer/Subject | Full DN in complex ASN.1 | Short printable strings (CAR/CHR) |
| Public Key | SubjectPublicKeyInfo with OID | 0x7F49 with nested OIDs and point |
| Validity | UTCTime fields in TBSCertificate | Simple YYMMDD strings |
| Authorization | Extensions (EKU, SAN) | Dedicated CHAT (bitmask for roles) |
| Signed Portion | Only TBSCertificate | All TLVs except signature |
| Size | Often 500–1000+ bytes | Typically 200–400 bytes (card-optimized) |
CVC trades flexibility for size and parsing speed – perfect for offline verification on the chip.
Practical Part: Get and Inspect a Real Test CVC
- Install a library to generate/view one (10-minute task):
- Python:
pip install pycvc(implements TR-03110 fully). - Or clone https://github.com/frankmorgner/openpace and build
cvc-print.
- Example with pycvc (run this to create a real test CVCA):
from pycvc.cvc import CVCertificateBuilder, ECCurve
from ecdsa import SigningKey, NIST256p # Or Brainpool
# Simple test self-signed CVCA
sk = SigningKey.generate(curve=NIST256p) # Use Brainpool for full TR-03110 compliance
vk = sk.verifying_key
builder = CVCertificateBuilder()
builder.set_car("UTCVCA00001")
builder.set_chr("UTCVCA00001")
builder.set_public_key(vk)
builder.set_effective_date("100101")
builder.set_expiration_date("251231")
cvc = builder.build(sk) # Signs it
print(cvc.to_der().hex()) # Outputs full hex of the CVC
This gives you a real DER-encoded CVC in hex. Dump it to file: with open('test_cvca.cvc', 'wb') as f: f.write(cvc.to_der())
- Simple TLV parser to inspect any CVC hex (extend for nested public key):
def parse_simple_tlv(data: bytes):
pos = 0
fields = {}
while pos < len(data):
tag = data[pos]
pos += 1
len_byte = data[pos]
pos += 1
if len_byte & 0x80: # Long form (rare in CVC)
continue # Skip for simplicity
length = len_byte
value = data[pos:pos + length]
pos += length
if tag == 0x42:
fields['CAR'] = value.decode('ascii')
elif tag == 0x5F20:
fields['CHR'] = value.decode('ascii')
elif tag == 0x5F25:
fields['Effective'] = value.decode('ascii')
elif tag == 0x5F24:
fields['Expiration'] = value.decode('ascii')
# Add 0x7F49 handling recursively for pubkey
return fields
# Use with your generated hex
hex_cvc = "your_hex_here_without_spaces"
print(parse_simple_tlv(bytes.fromhex(hex_cvc)))
Run the builder → get real hex → parse it. You'll see the exact fields match the example above.
This connects directly to signature verification: next time, we can add ECDSA verify on the body bytes using the public point from 0x7F49.
Spend your 10 minutes generating one with pycvc – it's the fastest way to have a real TR-03110-compliant CVC on your machine.
Recent Comments