This C# sample checks whether a certificate is a qualified certificate according to the EU Trusted Lists, and which qualified trust service branch its issuer belongs to, with the free .NET eIDAS Certificate Validator. It reads every .cer file of a folder (DER, PEM or Base64) and displays the answer for each one. Only the certificate is checked, not the files signed with it.

How it works

  • Create one EuTrustedListService and use it for every check: it keeps the Trusted Lists in memory. The lists are downloaded the first time and kept in a cache folder.
  • service.Check(certificate) takes an X509Certificate2. CheckFile takes a .cer file, Check(byte[]) the bytes of a certificate, and CheckAt evaluates the status at another moment, for example the time of a signature or of a time-stamp.
  • The answer is a CertificateQualificationResult: Status (QualifiedCertificate, QualifiedTrustService, NotQualified or NotListed), IsQualified, the Branches of the issuing service, the CertificateType, the Qscd status, and the trust service with its country, provider, status when the certificate was issued and the Trusted List where it appears.
  • The status is the one of the issuing service when the certificate was issued: an expired or revoked certificate keeps its qualified status. The revocation (OCSP, then CRL) is optional and informative.

C# code

/// <summary>
/// Creates the service that checks the certificates. Create one service and use it for every check: it keeps the Trusted Lists in memory.
/// </summary>
static EuTrustedListService CreateService()
{
    // 1. The default settings: the lists are kept in %LOCALAPPDATA%\EUTLValidator\cache and are checked again with the server after
    //    7 days (when a list did not change, nothing is downloaded). Only the list of the country of the certificate issuer is
    //    loaded, the other lists only if the certificate is not found in it.
    return new EuTrustedListService();

    // 2. Another cache folder and another refresh interval:
    // return new EuTrustedListService(@"D:\EUTL cache", TimeSpan.FromDays(1));

    // 3. All the settings (see EuTrustedListOptions): the revocation status (OCSP, then CRL) of every certificate, no download of
    //    issuer certificates, the proxy of the network, the check of the signatures of the lists...
    // return new EuTrustedListService(new EuTrustedListOptions { CheckRevocation = true });
}

/// <summary>
/// Gets the certificate to check. A certificate can come from various sources: choose one.
/// </summary>
static X509Certificate2 LoadCertificate(string certificateFile)
{
    // 1. Load the certificate from a .cer file, whatever its format is: DER, PEM (with BEGIN CERTIFICATE lines) or Base64 without lines.
    return CertificateReader.ReadFile(certificateFile);

    // 2. The bytes of a certificate (a database, an e-mail attachment, a web service):
    // return CertificateReader.Read(File.ReadAllBytes(certificateFile));

    // 3. A certificate that your application already has, for example the signing certificate of a signature, or one
    //    selected from the Windows certificate store: service.Check(certificate) takes any X509Certificate2.
}

/// <summary>
/// Displays the answer: is the certificate qualified, and which branch the issuing service belongs to.
/// </summary>
static void PrintQualification(CertificateQualificationResult result)
{
    // QualifiedCertificate: a qualified certificate.
    // QualifiedTrustService: the certificate of a qualified trust service (a CA, a time-stamping unit...), not of a user.
    // NotQualified: the issuer is listed, but the certificate is not qualified.
    // NotListed: neither the certificate nor its issuers are in the Trusted Lists.
    Console.WriteLine("Status: " + result.Status);
    Console.WriteLine("Is qualified: " + result.IsQualified);

    if (result.IsQualifiedCertificate)
    {
        // The purpose of the certificate and the place of its private key.
        Console.WriteLine("Certificate type: " + result.CertificateType + (result.ForLegalPerson ? " (legal person)" : " (natural person)"));
        Console.WriteLine("Private key in a QSCD: " + result.Qscd + (result.QscdManagedOnBehalf ? " (managed on behalf of the signatory)" : string.Empty));
    }

    // The branches of the issuing service (flags): the certificate for electronic signature, for electronic seal, for website
    // authentication, the validation and preservation services, the qualified time stamp, the remote management of the creation devices...
    Console.WriteLine("Branches of the issuing service: " + result.Branches);

    // The branch that corresponds to the purpose of this certificate (for a seal certificate, the seal branch).
    //Console.WriteLine("Branch of this certificate: " + result.CertificateBranch);

    // The place of the listed certificate in the chain: Self (the checked certificate is the service), Issuer, Intermediate or Root.
    //Console.WriteLine("Found in the Trusted List as: " + result.MatchedLevel);
}

/// <summary>
/// Displays the trust service and the Trusted List where it appears.
/// </summary>
static void PrintTrustService(CertificateQualificationResult result)
{
    TrustServiceInfo service = result.Service;
    if (service == null)
    {
        Console.WriteLine("No trust service was found.");
        return;
    }

    Console.WriteLine("Country: " + service.Country);
    Console.WriteLine("Trust service provider: " + service.TspName);
   //Console.WriteLine("Service: " + service.ServiceName + " [" + service.ServiceType + "]");

    // The status of the service when the certificate was issued is the one that counts. The current status is displayed for information.
    Console.WriteLine("Service status when the certificate was issued: " + service.StatusAtIssuance);
    Console.WriteLine("Service status now: " + service.CurrentStatus + " (since " + service.CurrentStatusStartingTime.ToString("yyyy-MM-dd") + ")");

    // The national Trusted List (XML) where the service appears.
    //TrustedListInfo list = service.TrustedList;
    //Console.WriteLine("Trusted List: " + list.Url);
    //Console.WriteLine("Trusted List number " + list.SequenceNumber + ", next update on " + list.NextUpdate);
    //Console.WriteLine("Trusted List signature is valid: " + list.SignatureVerified);
}

/// <summary>
/// Checks a certificate file and displays the result.
/// </summary>
static void CheckCertificate(EuTrustedListService service, string certificateFile)
{
    Console.WriteLine("Certificate file: " + Path.GetFileName(certificateFile));

    X509Certificate2 certificate = LoadCertificate(certificateFile);

    // The check. The Trusted Lists are loaded by the first check.
    CertificateQualificationResult result = service.Check(certificate);

    // Other ways to check (see also CheckOptions):
    //   service.CheckFile(certificateFile);                                 - directly from a file;
    //   service.CheckAt(certificate, new DateTime(2024, 5, 1));             - was it qualified at that moment? (a signature time, a time-stamp time)
    //   service.Check(certificate, new[] { intermediateCertificate });      - the issuer is looked for first among the certificates you give.

    PrintCertificateInformation(result);
    Console.WriteLine();

    PrintQualification(result);
    Console.WriteLine();

    PrintTrustService(result);
    Console.WriteLine();

    PrintDeclarations(result);
    //PrintReasons(result);

    // The revocation is checked only if the service was created with CheckRevocation = true. It never changes the qualified status.
    Console.WriteLine("Revocation: " + result.Revocation);
    Console.WriteLine();
}

Run the full sample. The complete Visual Studio solution EUTLValidator.sln is included in the download of the library, with the demo projects EUTLValidatorDemo.Net9 and EUTLValidatorDemo.NetFramework and two test certificates in the folder eIDAS_Test_Certificates. Download the .NET eIDAS Certificate Validator (free) · About the library

Frequently asked questions

Does it check the files signed with the certificate?

No. It checks the certificate: whether it is qualified and which branch its issuer belongs to. To create and verify signatures, use the .NET Digital Signature Library.

Does an expired or revoked certificate lose its qualified status?

No. The status is the one of the issuing service when the certificate was issued. The revocation is reported separately.

How do I check the certificate of a signed PDF?

Read the signatures of the document with the .NET Digital Signature Library, get the signing certificate of each signature and pass it to service.CheckAt with the signing time. The library page has the code.