CVC example

cyber-posts

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/AspectX.509 (ASN.1 DER)CVC (BER-TLV)
Issuer/SubjectFull DN in complex ASN.1Short printable strings (CAR/CHR)
Public KeySubjectPublicKeyInfo with OID0x7F49 with nested OIDs and point
ValidityUTCTime fields in TBSCertificateSimple YYMMDD strings
AuthorizationExtensions (EKU, SAN)Dedicated CHAT (bitmask for roles)
Signed PortionOnly TBSCertificateAll TLVs except signature
SizeOften 500–1000+ bytesTypically 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

  1. 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.
  1. 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())

  1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *