ETSI Trust Lists
The IDK supports ETSI TS 119 612 Trust Service Status Lists (TSL), the standard format used across the European Union for publishing trusted service providers. This enables validation of credentials issued by EU-recognized trust services.
What are ETSI Trust Lists?
ETSI Trust Lists (also called Trusted Lists or TSL) are standardized XML documents that publish information about trust service providers and their services. In the EU, each member state maintains a trusted list of qualified trust service providers (QTSPs) that meet eIDAS regulation requirements.
The EU also maintains a List of Trusted Lists (LOTL), which is a master index pointing to all member state trusted lists.
Trust List Hierarchy
Loading Trust Lists
- Android/kotlin
- iOS/Swift
val trustListService = session.component.trustListService
// Load the EU List of Trusted Lists
val lotl = trustListService.loadLotl(
url = "https://ec.europa.eu/tools/lotl/eu-lotl.xml"
)
// This automatically loads all referenced member state lists
println("Loaded ${lotl.trustedLists.size} member state lists")
// Access a specific country's list
val germanList = lotl.trustedLists.find { it.schemeTerritory == "DE" }
println("German TSPs: ${germanList?.trustServiceProviders?.size}")
let trustListService = session.component.trustListService
// Load the EU List of Trusted Lists
let lotl = try await trustListService.loadLotl(
url: "https://ec.europa.eu/tools/lotl/eu-lotl.xml"
)
// This automatically loads all referenced member state lists
print("Loaded \(lotl.trustedLists.count) member state lists")
// Access a specific country's list
if let germanList = lotl.trustedLists.first(where: { $0.schemeTerritory == "DE" }) {
print("German TSPs: \(germanList.trustServiceProviders.count)")
}
Loading Individual Lists
For scenarios where you only need specific country lists:
- Android/kotlin
- iOS/Swift
// Load a single country's trusted list
val germanList = trustListService.loadTrustedList(
url = "https://example.de/tsl.xml"
)
// Inspect the list
println("Territory: ${germanList.schemeTerritory}")
println("Operator: ${germanList.schemeOperatorName}")
println("Version: ${germanList.tslVersionIdentifier}")
println("Sequence: ${germanList.tslSequenceNumber}")
println("Issue Date: ${germanList.listIssueDateTime}")
println("Next Update: ${germanList.nextUpdate}")
// Load a single country's trusted list
let germanList = try await trustListService.loadTrustedList(
url: "https://example.de/tsl.xml"
)
// Inspect the list
print("Territory: \(germanList.schemeTerritory)")
print("Operator: \(germanList.schemeOperatorName)")
print("Version: \(germanList.tslVersionIdentifier)")
print("Sequence: \(germanList.tslSequenceNumber)")
print("Issue Date: \(germanList.listIssueDateTime)")
print("Next Update: \(germanList.nextUpdate)")
Querying Trust Service Providers
- Android/kotlin
- iOS/Swift
// Find all qualified trust service providers
val qualifiedProviders = lotl.findProviders {
status = TrustServiceStatus.QUALIFIED
}
qualifiedProviders.forEach { provider ->
println("Provider: ${provider.name}")
println(" Country: ${provider.schemeTerritory}")
println(" Services: ${provider.services.size}")
}
// Find providers offering specific service types
val signatureProviders = lotl.findProviders {
serviceType = ServiceType.QUALIFIED_CERTIFICATE_FOR_ELECTRONIC_SIGNATURE
}
// Find providers by certificate
val matchingProviders = lotl.findProvidersByCertificate(
certificate = issuerCertificate
)
// Find all qualified trust service providers
let qualifiedProviders = lotl.findProviders { query in
query.status = .qualified
}
for provider in qualifiedProviders {
print("Provider: \(provider.name)")
print(" Country: \(provider.schemeTerritory)")
print(" Services: \(provider.services.count)")
}
// Find providers offering specific service types
let signatureProviders = lotl.findProviders { query in
query.serviceType = .qualifiedCertificateForElectronicSignature
}
// Find providers by certificate
let matchingProviders = try lotl.findProvidersByCertificate(
certificate: issuerCertificate
)
Trust Service Status
ETSI defines specific status values for trust services:
| Status | Description |
|---|---|
GRANTED | Service is currently granted/active |
WITHDRAWN | Service has been withdrawn |
DEPRECATED | Service is deprecated but may still be valid |
RECOGNISEDAT NATIONALLEVEL | Recognized at national level |
QUALIFIED | Service is qualified under eIDAS |
- Android/kotlin
- iOS/Swift
// Check if a service is currently valid
val service = provider.services.first()
when (service.currentStatus) {
TrustServiceStatus.GRANTED,
TrustServiceStatus.QUALIFIED -> {
println("Service is active and trusted")
}
TrustServiceStatus.WITHDRAWN -> {
println("Service has been withdrawn")
}
TrustServiceStatus.DEPRECATED -> {
println("Service is deprecated - check validity period")
}
}
// Check status history
service.statusHistory.forEach { statusEntry ->
println("${statusEntry.status} from ${statusEntry.startDate}")
}
// Check if a service is currently valid
let service = provider.services.first!
switch service.currentStatus {
case .granted, .qualified:
print("Service is active and trusted")
case .withdrawn:
print("Service has been withdrawn")
case .deprecated:
print("Service is deprecated - check validity period")
default:
break
}
// Check status history
for statusEntry in service.statusHistory {
print("\(statusEntry.status) from \(statusEntry.startDate)")
}
Trust Validation with TSL
Use trust lists for credential validation:
- Android/kotlin
- iOS/Swift
// Configure trust validator with ETSI trust lists
val trustValidator = TrustValidator {
etsiTrustLists {
// Load EU LOTL
lotlUrl = "https://ec.europa.eu/tools/lotl/eu-lotl.xml"
// Only trust qualified services
minimumStatus = TrustServiceStatus.QUALIFIED
// Cache settings
cacheEnabled = true
cacheDuration = Duration.ofHours(24)
// Refresh in background
backgroundRefresh = true
}
}
// Validate a certificate against trust lists
val result = trustValidator.validateTrust(
certificate = issuerCertificate,
purpose = TrustPurpose.CREDENTIAL_ISSUER
)
when (result) {
is TrustResult.Trusted -> {
// Certificate found in trust list
println("Issuer: ${result.trustServiceProvider}")
println("Service: ${result.trustService}")
println("Country: ${result.schemeTerritory}")
println("Status: ${result.serviceStatus}")
}
is TrustResult.NotTrusted -> {
println("Not found in trust lists")
}
}
// Configure trust validator with ETSI trust lists
let trustValidator = TrustValidator { config in
config.etsiTrustLists { etsi in
// Load EU LOTL
etsi.lotlUrl = "https://ec.europa.eu/tools/lotl/eu-lotl.xml"
// Only trust qualified services
etsi.minimumStatus = .qualified
// Cache settings
etsi.cacheEnabled = true
etsi.cacheDuration = 24 * 60 * 60 // 24 hours
// Refresh in background
etsi.backgroundRefresh = true
}
}
// Validate a certificate against trust lists
let result = try await trustValidator.validateTrust(
certificate: issuerCertificate,
purpose: .credentialIssuer
)
switch result {
case .trusted(let trust):
// Certificate found in trust list
print("Issuer: \(trust.trustServiceProvider)")
print("Service: \(trust.trustService)")
print("Country: \(trust.schemeTerritory)")
print("Status: \(trust.serviceStatus)")
case .notTrusted:
print("Not found in trust lists")
}
Custom Trust Lists
Create custom trust lists following the ETSI format:
- Android/kotlin
- iOS/Swift
// Build a custom trust list
val customList = TrustedListBuilder()
.schemeOperatorName("My Organization")
.schemeTerritory("XX")
.tslVersionIdentifier(5)
.tslSequenceNumber(1)
.listIssueDateTime(Instant.now())
.nextUpdate(Instant.now().plus(Duration.ofDays(180)))
.trustServiceProvider {
name = "Internal Identity Provider"
tradeName = "Internal IDP"
electronicAddress = "https://idp.example.com"
service {
type = ServiceType.QUALIFIED_CERTIFICATE_FOR_ELECTRONIC_SIGNATURE
name = "Identity Credential Service"
status = TrustServiceStatus.GRANTED
startDate = Instant.now()
// Add service certificates
certificates = listOf(issuerCertificate)
}
}
.build()
// Register custom list with trust service
trustListService.registerCustomList(customList)
// Build a custom trust list
let customList = TrustedListBuilder()
.schemeOperatorName(name: "My Organization")
.schemeTerritory(territory: "XX")
.tslVersionIdentifier(version: 5)
.tslSequenceNumber(sequence: 1)
.listIssueDateTime(date: Date())
.nextUpdate(date: Date().addingTimeInterval(180 * 24 * 60 * 60))
.trustServiceProvider { provider in
provider.name = "Internal Identity Provider"
provider.tradeName = "Internal IDP"
provider.electronicAddress = "https://idp.example.com"
provider.service { service in
service.type = .qualifiedCertificateForElectronicSignature
service.name = "Identity Credential Service"
service.status = .granted
service.startDate = Date()
// Add service certificates
service.certificates = [issuerCertificate]
}
}
.build()
// Register custom list with trust service
trustListService.registerCustomList(list: customList)
Trust List Caching
Configure caching for performance:
- Android/kotlin
- iOS/Swift
val trustListService = TrustListService {
// Enable caching
cache {
enabled = true
directory = context.cacheDir.resolve("trust-lists")
maxAge = Duration.ofHours(24)
// Keep expired cache as fallback
staleWhileRevalidate = true
}
// Background refresh
refresh {
enabled = true
interval = Duration.ofHours(12)
retryOnError = true
maxRetries = 3
}
// Network settings
http {
timeout = Duration.ofSeconds(30)
userAgent = "MyApp/1.0 IDK/0.13"
}
}
// Force refresh if needed
trustListService.refresh()
// Check cache status
val cacheStatus = trustListService.cacheStatus
println("Last update: ${cacheStatus.lastUpdate}")
println("Next refresh: ${cacheStatus.nextRefresh}")
let trustListService = TrustListService { config in
// Enable caching
config.cache { cache in
cache.enabled = true
cache.directory = FileManager.default.temporaryDirectory.appendingPathComponent("trust-lists")
cache.maxAge = 24 * 60 * 60 // 24 hours
// Keep expired cache as fallback
cache.staleWhileRevalidate = true
}
// Background refresh
config.refresh { refresh in
refresh.enabled = true
refresh.interval = 12 * 60 * 60 // 12 hours
refresh.retryOnError = true
refresh.maxRetries = 3
}
// Network settings
config.http { http in
http.timeout = 30
http.userAgent = "MyApp/1.0 IDK/0.13"
}
}
// Force refresh if needed
try await trustListService.refresh()
// Check cache status
let cacheStatus = trustListService.cacheStatus
print("Last update: \(cacheStatus.lastUpdate)")
print("Next refresh: \(cacheStatus.nextRefresh)")
Configuration
Configure trust list handling via properties:
# ETSI Trust Lists
trust.etsi.enabled=true
trust.etsi.lotl.url=https://ec.europa.eu/tools/lotl/eu-lotl.xml
# Caching
trust.etsi.cache.enabled=true
trust.etsi.cache.directory=${app.data.dir}/trust-lists
trust.etsi.cache.max-age-hours=24
# Background refresh
trust.etsi.refresh.enabled=true
trust.etsi.refresh.interval-hours=12
# Validation
trust.etsi.minimum-status=QUALIFIED
trust.etsi.verify-signatures=true
Best Practices
Cache trust lists aggressively. Trust lists are large and change infrequently. Use caching to improve performance and reduce network traffic.
Enable background refresh. Keep trust lists current by refreshing in the background, so validation is never blocked by network requests.
Verify trust list signatures. Trust lists are signed by the scheme operator. Always verify signatures to prevent tampering.
Handle offline scenarios. Use stale cache data when network is unavailable, but log warnings for monitoring.
Monitor trust list updates. Track when new versions are published and review changes that might affect your application.