be.fedict.eid.tsl.Tsl2PdfExporter.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.tsl.Tsl2PdfExporter.java

Source

/*
 * eID TSL Project.
 * Copyright (C) 2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.tsl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.openssl.PEMWriter;
import org.joda.time.DateTime;
import org.w3c.dom.Element;

import be.fedict.eid.tsl.jaxb.ecc.CriteriaListType;
import be.fedict.eid.tsl.jaxb.ecc.PoliciesListType;
import be.fedict.eid.tsl.jaxb.ecc.QualificationElementType;
import be.fedict.eid.tsl.jaxb.ecc.QualificationsType;
import be.fedict.eid.tsl.jaxb.ecc.QualifierType;
import be.fedict.eid.tsl.jaxb.ecc.QualifiersType;
import be.fedict.eid.tsl.jaxb.tsl.AdditionalServiceInformationType;
import be.fedict.eid.tsl.jaxb.tsl.ExtensionType;
import be.fedict.eid.tsl.jaxb.tsl.ExtensionsListType;
import be.fedict.eid.tsl.jaxb.tsl.InternationalNamesType;
import be.fedict.eid.tsl.jaxb.tsl.NonEmptyMultiLangURIType;
import be.fedict.eid.tsl.jaxb.tsl.PostalAddressType;
import be.fedict.eid.tsl.jaxb.tsl.ServiceHistoryInstanceType;
import be.fedict.eid.tsl.jaxb.tsl.ServiceHistoryType;
import be.fedict.eid.tsl.jaxb.tsl.TSPServiceInformationType;
import be.fedict.eid.tsl.jaxb.xades.ObjectIdentifierType;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

public class Tsl2PdfExporter {

    private static final Log LOG = LogFactory.getLog(Tsl2PdfExporter.class);

    public Tsl2PdfExporter() {
        initializeFontResources();
        title0Font = FontFactory.getFont("DejaVuSerifCondensed-Bold", BaseFont.IDENTITY_H, true, 30, Font.BOLD);
        title1Font = FontFactory.getFont("DejaVuSerifCondensed-BoldItalic", BaseFont.IDENTITY_H, true, 16,
                Font.BOLD | Font.ITALIC);
        title2Font = FontFactory.getFont("DejaVuSerifCondensed-BoldItalic", BaseFont.IDENTITY_H, true, 16,
                Font.BOLD | Font.ITALIC);
        title3Font = FontFactory.getFont("DejaVuSerifCondensed-Italic", BaseFont.IDENTITY_H, true, 16, Font.ITALIC);
        title4Font = FontFactory.getFont("DejaVuSerifCondensed-Italic", BaseFont.IDENTITY_H, true, 12, Font.BOLD);
        labelFont = FontFactory.getFont("DejaVuSerifCondensed-Italic", BaseFont.IDENTITY_H, true, 11, Font.ITALIC);
        valueFont = FontFactory.getFont("DejaVuSerifCondensed", BaseFont.IDENTITY_H, true, 11, Font.NORMAL);
        monoFont = FontFactory.getFont("DejaVuSansMono", BaseFont.IDENTITY_H, true, 5, Font.NORMAL);
        headerFooterFont = FontFactory.getFont("DejaVuSerifCondensed", BaseFont.IDENTITY_H, true, 10, Font.NORMAL);
    }

    private static void initializeFontResources() {
        try {
            final File tmpDir = createTempDirectory();
            loadFont(tmpDir, "DejaVuSerifCondensed-Bold");
            loadFont(tmpDir, "DejaVuSerifCondensed-BoldItalic");
            loadFont(tmpDir, "DejaVuSerifCondensed-Italic");
            loadFont(tmpDir, "DejaVuSerifCondensed");
            loadFont(tmpDir, "DejaVuSansMono");
            FontFactory.registerDirectory(tmpDir.getAbsolutePath());
        } catch (Exception e) {
            throw new RuntimeException("when initializing fonts", e);
        }
    }

    private static void loadFont(final File dir, final String name) {
        final String fontBase = "/org/dejavu/font/";
        final File file = new File(dir, name + ".ttf");
        final InputStream ttfStream = Tsl2PdfExporter.class.getResourceAsStream(fontBase + name + ".ttf");
        try {
            final OutputStream fileStream = new FileOutputStream(file);
            try {
                IOUtils.copy(ttfStream, fileStream);
            } finally {
                IOUtils.closeQuietly(fileStream);
            }
        } catch (IOException e) {
            throw new RuntimeException("error initializing font", e);
        } finally {
            IOUtils.closeQuietly(ttfStream);
        }
        file.deleteOnExit();
    }

    private static final int BORDER = 0;

    protected final Font title0Font;
    protected final Font title1Font;
    protected final Font title2Font;
    protected final Font title3Font;
    protected final Font title4Font;
    protected final Font labelFont;
    protected final Font valueFont;
    protected final Font monoFont;
    protected final Font headerFooterFont;

    /**
     * Produce a human readable export of the given tsl to the given file.
     * 
     * @param tsl
     *            the TrustServiceList to export
     * @param pdfFile
     *            the file to generate
     * @return
     * @throws IOException
     */
    public void humanReadableExport(final TrustServiceList tsl, final File pdfFile) {
        Document document = new Document();
        OutputStream outputStream;
        try {
            outputStream = new FileOutputStream(pdfFile);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("file not found: " + pdfFile.getAbsolutePath(), e);
        }
        try {
            final PdfWriter pdfWriter = PdfWriter.getInstance(document, outputStream);
            pdfWriter.setPDFXConformance(PdfWriter.PDFA1B);

            // title
            final EUCountry country = EUCountry.valueOf(tsl.getSchemeTerritory());
            final String title = country.getShortSrcLangName() + " (" + country.getShortEnglishName()
                    + "): Trusted List";

            Phrase footerPhrase = new Phrase("PDF document generated on " + new Date().toString() + ", page ",
                    headerFooterFont);
            HeaderFooter footer = new HeaderFooter(footerPhrase, true);
            document.setFooter(footer);

            Phrase headerPhrase = new Phrase(title, headerFooterFont);
            HeaderFooter header = new HeaderFooter(headerPhrase, false);
            document.setHeader(header);

            document.open();
            addTitle(title, title0Font, Paragraph.ALIGN_CENTER, 0, 20, document);

            addLongItem("Scheme name", tsl.getSchemeName(), document);
            addLongItem("Legal Notice", tsl.getLegalNotice(), document);

            // information table
            PdfPTable informationTable = createInfoTable();
            addItemRow("Scheme territory", tsl.getSchemeTerritory(), informationTable);
            addItemRow("Scheme status determination approach",
                    substringAfter(tsl.getStatusDeterminationApproach(), "StatusDetn/"), informationTable);
            /*
            final List<String> schemeTypes = new ArrayList<String>();
            for (final String schemeType : tsl.getSchemeTypes()) {
               schemeTypes.add(schemeType);
            }
            */
            final List<String> schemeTypes = new ArrayList<String>();
            List<NonEmptyMultiLangURIType> uris = tsl.getSchemeTypes();
            for (NonEmptyMultiLangURIType uri : uris) {
                schemeTypes.add(uri.getValue());
            }
            addItemRow("Scheme type community rules", schemeTypes, informationTable);

            addItemRow("Issue date", tsl.getListIssueDateTime().toString(), informationTable);
            addItemRow("Next update", tsl.getNextUpdate().toString(), informationTable);
            addItemRow("Historical information period", tsl.getHistoricalInformationPeriod().toString() + " days",
                    informationTable);
            addItemRow("Sequence number", tsl.getSequenceNumber().toString(), informationTable);
            addItemRow("Scheme information URIs", tsl.getSchemeInformationUris(), informationTable);

            document.add(informationTable);

            addTitle("Scheme Operator", title1Font, Paragraph.ALIGN_CENTER, 0, 10, document);

            informationTable = createInfoTable();
            addItemRow("Scheme operator name", tsl.getSchemeOperatorName(), informationTable);
            PostalAddressType schemeOperatorPostalAddress = tsl.getSchemeOperatorPostalAddress(Locale.ENGLISH);
            addItemRow("Scheme operator street address", schemeOperatorPostalAddress.getStreetAddress(),
                    informationTable);
            addItemRow("Scheme operator postal code", schemeOperatorPostalAddress.getPostalCode(),
                    informationTable);
            addItemRow("Scheme operator locality", schemeOperatorPostalAddress.getLocality(), informationTable);
            addItemRow("Scheme operator state", schemeOperatorPostalAddress.getStateOrProvince(), informationTable);
            addItemRow("Scheme operator country", schemeOperatorPostalAddress.getCountryName(), informationTable);

            List<String> schemeOperatorElectronicAddressess = tsl.getSchemeOperatorElectronicAddresses();
            addItemRow("Scheme operator contact", schemeOperatorElectronicAddressess, informationTable);
            document.add(informationTable);

            addTitle("Trust Service Providers", title1Font, Paragraph.ALIGN_CENTER, 10, 2, document);

            List<TrustServiceProvider> trustServiceProviders = tsl.getTrustServiceProviders();
            for (TrustServiceProvider trustServiceProvider : trustServiceProviders) {
                addTitle(trustServiceProvider.getName(), title1Font, Paragraph.ALIGN_LEFT, 10, 2, document);

                PdfPTable providerTable = createInfoTable();
                addItemRow("Service provider trade name", trustServiceProvider.getTradeNames(), providerTable);
                addItemRow("Information URI", trustServiceProvider.getInformationUris(), providerTable);
                PostalAddressType postalAddress = trustServiceProvider.getPostalAddress();
                addItemRow("Service provider street address", postalAddress.getStreetAddress(), providerTable);
                addItemRow("Service provider postal code", postalAddress.getPostalCode(), providerTable);
                addItemRow("Service provider locality", postalAddress.getLocality(), providerTable);
                addItemRow("Service provider state", postalAddress.getStateOrProvince(), providerTable);
                addItemRow("Service provider country", postalAddress.getCountryName(), providerTable);
                document.add(providerTable);

                List<TrustService> trustServices = trustServiceProvider.getTrustServices();
                for (TrustService trustService : trustServices) {
                    addTitle(trustService.getName(), title2Font, Paragraph.ALIGN_LEFT, 10, 2, document);
                    PdfPTable serviceTable = createInfoTable();
                    addItemRow("Type", substringAfter(trustService.getType(), "Svctype/"), serviceTable);
                    addItemRow("Status", substringAfter(trustService.getStatus(), "Svcstatus/"), serviceTable);
                    addItemRow("Status starting time", trustService.getStatusStartingTime().toString(),
                            serviceTable);
                    document.add(serviceTable);

                    addTitle("Service digital identity (X509)", title3Font, Paragraph.ALIGN_LEFT, 2, 0, document);
                    final X509Certificate certificate = trustService.getServiceDigitalIdentity();
                    final PdfPTable serviceIdentityTable = createInfoTable();
                    addItemRow("Version", Integer.toString(certificate.getVersion()), serviceIdentityTable);
                    addItemRow("Serial number", certificate.getSerialNumber().toString(), serviceIdentityTable);
                    addItemRow("Signature algorithm", certificate.getSigAlgName(), serviceIdentityTable);
                    addItemRow("Issuer", certificate.getIssuerX500Principal().toString(), serviceIdentityTable);
                    addItemRow("Valid from", certificate.getNotBefore().toString(), serviceIdentityTable);
                    addItemRow("Valid to", certificate.getNotAfter().toString(), serviceIdentityTable);
                    addItemRow("Subject", certificate.getSubjectX500Principal().toString(), serviceIdentityTable);
                    addItemRow("Public key", certificate.getPublicKey().toString(), serviceIdentityTable);
                    // TODO certificate policies
                    addItemRow("Subject key identifier", toHex(getSKId(certificate)), serviceIdentityTable);
                    addItemRow("CRL distribution points", getCrlDistributionPoints(certificate),
                            serviceIdentityTable);
                    addItemRow("Authority key identifier", toHex(getAKId(certificate)), serviceIdentityTable);
                    addItemRow("Key usage", getKeyUsage(certificate), serviceIdentityTable);
                    addItemRow("Basic constraints", getBasicConstraints(certificate), serviceIdentityTable);

                    byte[] encodedCertificate;
                    try {
                        encodedCertificate = certificate.getEncoded();
                    } catch (CertificateEncodingException e) {
                        throw new RuntimeException("cert: " + e.getMessage(), e);
                    }
                    addItemRow("SHA1 Thumbprint", DigestUtils.shaHex(encodedCertificate), serviceIdentityTable);
                    addItemRow("SHA256 Thumbprint", DigestUtils.sha256Hex(encodedCertificate),
                            serviceIdentityTable);
                    document.add(serviceIdentityTable);

                    //add Scheme service definition 
                    if (null != trustService.getSchemeServiceDefinitionURI()) {
                        addTitle("Scheme Service Definition URI", title3Font, Paragraph.ALIGN_LEFT, 2, 0, document);
                        final PdfPTable schemeServiceDefinitionURITabel = createInfoTable();
                        for (NonEmptyMultiLangURIType uri : trustService.getSchemeServiceDefinitionURI().getURI()) {
                            addItemRow(uri.getLang(), uri.getValue(), schemeServiceDefinitionURITabel);
                        }
                        document.add(schemeServiceDefinitionURITabel);
                    }

                    List<ExtensionType> extensions = trustService.getExtensions();
                    for (ExtensionType extension : extensions) {
                        printExtension(extension, document);
                    }

                    addLongMonoItem("The decoded certificate:", certificate.toString(), document);
                    addLongMonoItem("The certificate in PEM format:", toPem(certificate), document);

                    ServiceHistoryType serviceHistoryType = trustService.getServiceHistoryInstanceType();

                    if (null != serviceHistoryType) {

                        for (ServiceHistoryInstanceType serviceHistoryInstanceType : serviceHistoryType
                                .getServiceHistoryInstance()) {
                            PdfPTable serviceHistoryTable = createInfoTable();

                            //Service approval history information
                            addTitle("Service approval history information", title3Font, Paragraph.ALIGN_LEFT, 10,
                                    2, document);

                            // service type identifier
                            //5.6.2 Service name
                            InternationalNamesType i18nServiceName = serviceHistoryInstanceType.getServiceName();
                            String servName = TrustServiceListUtils.getValue(i18nServiceName, Locale.ENGLISH);
                            addItemRow("Name", servName, serviceHistoryTable);
                            //5.6.1 Service type identifier
                            addItemRow("Type", substringAfter(serviceHistoryInstanceType.getServiceTypeIdentifier(),
                                    "Svctype/"), serviceHistoryTable);
                            addItemRow("Status", serviceHistoryInstanceType.getServiceStatus(),
                                    serviceHistoryTable);
                            //5.6.4 Service previous status
                            addItemRow("Previous status", serviceHistoryInstanceType.getServiceStatus(),
                                    serviceHistoryTable);
                            //5.6.5 Previous status starting date and time
                            addItemRow(
                                    "Previous starting time", new DateTime(serviceHistoryInstanceType
                                            .getStatusStartingTime().toGregorianCalendar()).toString(),
                                    serviceHistoryTable);
                            //5.6.3 Service digital identity
                            final X509Certificate previousCertificate = trustService.getServiceDigitalIdentity(
                                    serviceHistoryInstanceType.getServiceDigitalIdentity());

                            document.add(serviceHistoryTable);

                            addTitle("Service digital identity (X509)", title4Font, Paragraph.ALIGN_LEFT, 2, 0,
                                    document);

                            final PdfPTable serviceIdentityTableHistory = createInfoTable();
                            addItemRow("Version", Integer.toString(previousCertificate.getVersion()),
                                    serviceIdentityTableHistory);
                            addItemRow("Serial number", previousCertificate.getSerialNumber().toString(),
                                    serviceIdentityTableHistory);
                            addItemRow("Signature algorithm", previousCertificate.getSigAlgName(),
                                    serviceIdentityTableHistory);
                            addItemRow("Issuer", previousCertificate.getIssuerX500Principal().toString(),
                                    serviceIdentityTableHistory);
                            addItemRow("Valid from", previousCertificate.getNotBefore().toString(),
                                    serviceIdentityTableHistory);
                            addItemRow("Valid to", previousCertificate.getNotAfter().toString(),
                                    serviceIdentityTableHistory);
                            addItemRow("Subject", previousCertificate.getSubjectX500Principal().toString(),
                                    serviceIdentityTableHistory);
                            addItemRow("Public key", previousCertificate.getPublicKey().toString(),
                                    serviceIdentityTableHistory);
                            // TODO certificate policies
                            addItemRow("Subject key identifier", toHex(getSKId(previousCertificate)),
                                    serviceIdentityTableHistory);
                            addItemRow("CRL distribution points", getCrlDistributionPoints(previousCertificate),
                                    serviceIdentityTableHistory);
                            addItemRow("Authority key identifier", toHex(getAKId(previousCertificate)),
                                    serviceIdentityTableHistory);
                            addItemRow("Key usage", getKeyUsage(previousCertificate), serviceIdentityTableHistory);
                            addItemRow("Basic constraints", getBasicConstraints(previousCertificate),
                                    serviceIdentityTableHistory);

                            byte[] encodedHistoryCertificate;
                            try {
                                encodedHistoryCertificate = previousCertificate.getEncoded();
                            } catch (CertificateEncodingException e) {
                                throw new RuntimeException("cert: " + e.getMessage(), e);
                            }
                            addItemRow("SHA1 Thumbprint", DigestUtils.shaHex(encodedHistoryCertificate),
                                    serviceIdentityTableHistory);
                            addItemRow("SHA256 Thumbprint", DigestUtils.sha256Hex(encodedHistoryCertificate),
                                    serviceIdentityTableHistory);
                            document.add(serviceIdentityTableHistory);

                            ExtensionsListType previousExtensions = serviceHistoryInstanceType
                                    .getServiceInformationExtensions();
                            if (null != previousExtensions) {
                                for (ExtensionType extension : previousExtensions.getExtension()) {
                                    printExtension(extension, document);
                                }
                            }

                            addLongMonoItem("The decoded certificate:", previousCertificate.toString(), document);
                            addLongMonoItem("The certificate in PEM format:", toPem(previousCertificate), document);
                        }
                    }
                }
            }

            X509Certificate signerCertificate = tsl.verifySignature();
            if (null != signerCertificate) {
                Paragraph tslSignerTitle = new Paragraph("Trusted List Signer", title1Font);
                tslSignerTitle.setAlignment(Paragraph.ALIGN_CENTER);
                document.add(tslSignerTitle);

                final PdfPTable signerTable = createInfoTable();
                addItemRow("Subject", signerCertificate.getSubjectX500Principal().toString(), signerTable);
                addItemRow("Issuer", signerCertificate.getIssuerX500Principal().toString(), signerTable);
                addItemRow("Not before", signerCertificate.getNotBefore().toString(), signerTable);
                addItemRow("Not after", signerCertificate.getNotAfter().toString(), signerTable);
                addItemRow("Serial number", signerCertificate.getSerialNumber().toString(), signerTable);
                addItemRow("Version", Integer.toString(signerCertificate.getVersion()), signerTable);
                byte[] encodedPublicKey = signerCertificate.getPublicKey().getEncoded();
                addItemRow("Public key SHA1 Thumbprint", DigestUtils.shaHex(encodedPublicKey), signerTable);
                addItemRow("Public key SHA256 Thumbprint", DigestUtils.sha256Hex(encodedPublicKey), signerTable);
                document.add(signerTable);

                addLongMonoItem("The decoded certificate:", signerCertificate.toString(), document);
                addLongMonoItem("The certificate in PEM format:", toPem(signerCertificate), document);
                addLongMonoItem("The public key in PEM format:", toPem(signerCertificate.getPublicKey()), document);
            }

            document.close();
        } catch (DocumentException e) {
            throw new RuntimeException("PDF document error: " + e.getMessage(), e);
        } catch (Exception e) {
            throw new RuntimeException("Exception: " + e.getMessage(), e);
        }
    }

    private static final QName ADDITIONAL_SERVICE_INFORMATION_QNAME = new QName("http://uri.etsi.org/02231/v2#",
            "AdditionalServiceInformation");

    private void printExtension(ExtensionType extension, Document document) throws DocumentException {

        List<Object> contentList = extension.getContent();
        for (Object content : contentList) {
            LOG.debug("extension content: " + content.getClass().getName());
            if (content instanceof JAXBElement<?>) {
                JAXBElement<?> element = (JAXBElement<?>) content;
                LOG.debug("QName: " + element.getName());
                if (true == ADDITIONAL_SERVICE_INFORMATION_QNAME.equals(element.getName())) {
                    addTitle("Extension (critical: " + extension.isCritical() + ")", title3Font,
                            Paragraph.ALIGN_LEFT, 0, 0, document);
                    addTitle("Additional service information", title4Font, Paragraph.ALIGN_LEFT, 0, 0, document);
                    AdditionalServiceInformationType additionalServiceInformation = (AdditionalServiceInformationType) element
                            .getValue();
                    LOG.debug("information value: " + additionalServiceInformation.getInformationValue());
                    NonEmptyMultiLangURIType multiLangUri = additionalServiceInformation.getURI();
                    LOG.debug("URI : " + multiLangUri.getValue() + " (language: " + multiLangUri.getLang() + ")");
                    document.add(new Paragraph(
                            multiLangUri.getValue().substring(
                                    multiLangUri.getValue().indexOf("SvcInfoExt/") + "SvcInfoExt/".length()),
                            this.valueFont));
                } else {
                    addTitle("Extension (critical: " + extension.isCritical() + ")", title3Font,
                            Paragraph.ALIGN_LEFT, 0, 0, document);
                    addTitle("Qualifications", title4Font, Paragraph.ALIGN_LEFT, 0, 0, document);

                    LOG.debug("element namespace: " + element.getName());
                    LOG.debug("element name: " + element.getScope());
                    if ("http://uri.etsi.org/TrstSvc/SvcInfoExt/eSigDir-1999-93-EC-TrustedList/#"
                            .equals(element.getName().getNamespaceURI())
                            && "Qualifications".equals(element.getName().getLocalPart())) {
                        QualificationsType qualifications = (QualificationsType) element.getValue();
                        List<QualificationElementType> qualificationElements = qualifications
                                .getQualificationElement();
                        for (QualificationElementType qualificationElement : qualificationElements) {
                            QualifiersType qualifiers = qualificationElement.getQualifiers();
                            List<QualifierType> qualifierList = qualifiers.getQualifier();
                            for (QualifierType qualifier : qualifierList) {
                                document.add(new Paragraph(
                                        "Qualifier: " + qualifier.getUri().substring(
                                                qualifier.getUri().indexOf("SvcInfoExt/") + "SvcInfoExt/".length()),
                                        this.valueFont));
                            }

                            CriteriaListType criteriaList = qualificationElement.getCriteriaList();
                            String description = criteriaList.getDescription();
                            if (null != description) {
                                document.add(new Paragraph("Criterial List Description", this.labelFont));
                                document.add(new Paragraph(description, this.valueFont));
                            }
                            document.add(new Paragraph("Assert: " + criteriaList.getAssert(), this.valueFont));
                            List<PoliciesListType> policySet = criteriaList.getPolicySet();
                            for (PoliciesListType policiesList : policySet) {
                                List<ObjectIdentifierType> oids = policiesList.getPolicyIdentifier();
                                for (ObjectIdentifierType oid : oids) {
                                    document.add(new Paragraph("Policy OID: " + oid.getIdentifier().getValue(),
                                            this.valueFont));
                                }
                            }
                        }
                    }

                }

            } else if (content instanceof Element) {
                addTitle("Qualifications", title4Font, Paragraph.ALIGN_LEFT, 0, 0, document);
                Element element = (Element) content;
                LOG.debug("element namespace: " + element.getNamespaceURI());
                LOG.debug("element name: " + element.getLocalName());
                if ("http://uri.etsi.org/TrstSvc/SvcInfoExt/eSigDir-1999-93-EC-TrustedList/#"
                        .equals(element.getNamespaceURI()) && "Qualifications".equals(element.getLocalName())) {
                    try {
                        QualificationsType qualifications = unmarshallQualifications(element);
                        List<QualificationElementType> qualificationElements = qualifications
                                .getQualificationElement();
                        for (QualificationElementType qualificationElement : qualificationElements) {
                            QualifiersType qualifiers = qualificationElement.getQualifiers();
                            List<QualifierType> qualifierList = qualifiers.getQualifier();
                            for (QualifierType qualifier : qualifierList) {
                                document.add(new Paragraph(
                                        "Qualifier: " + qualifier.getUri().substring(
                                                qualifier.getUri().indexOf("SvcInfoExt/") + "SvcInfoExt/".length()),
                                        this.valueFont));
                            }

                            CriteriaListType criteriaList = qualificationElement.getCriteriaList();
                            String description = criteriaList.getDescription();
                            if (null != description) {
                                document.add(new Paragraph("Criterial List Description", this.labelFont));
                                document.add(new Paragraph(description, this.valueFont));
                            }
                            document.add(new Paragraph("Assert: " + criteriaList.getAssert(), this.valueFont));
                            List<PoliciesListType> policySet = criteriaList.getPolicySet();
                            for (PoliciesListType policiesList : policySet) {
                                List<ObjectIdentifierType> oids = policiesList.getPolicyIdentifier();
                                for (ObjectIdentifierType oid : oids) {
                                    document.add(new Paragraph("Policy OID: " + oid.getIdentifier().getValue(),
                                            this.valueFont));
                                }
                            }
                        }
                    } catch (JAXBException e) {
                        LOG.error("JAXB error: " + e.getMessage(), e);
                    }
                }
            }
        }
    }

    private QualificationsType unmarshallQualifications(Element element) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(be.fedict.eid.tsl.jaxb.ecc.ObjectFactory.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        JAXBElement<QualificationsType> jaxbElement = (JAXBElement<QualificationsType>) unmarshaller
                .unmarshal(element);
        QualificationsType qualifications = jaxbElement.getValue();
        return qualifications;
    }

    private String toPem(Object object) {
        StringWriter buffer = new StringWriter();
        try {
            PEMWriter writer = new PEMWriter(buffer);
            writer.writeObject(object);
            writer.close();
            return buffer.toString();
        } catch (Exception e) {
            throw new RuntimeException("Cannot convert public key to PEM format: " + e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(buffer);
        }
    }

    private byte[] getSKId(final X509Certificate cert) throws IOException {
        final byte[] extValue = cert.getExtensionValue(X509Extensions.SubjectKeyIdentifier.getId());
        if (extValue != null) {
            final ASN1OctetString str = ASN1OctetString
                    .getInstance(new ASN1InputStream(new ByteArrayInputStream(extValue)).readObject());
            final SubjectKeyIdentifier keyId = SubjectKeyIdentifier
                    .getInstance(new ASN1InputStream(new ByteArrayInputStream(str.getOctets())).readObject());
            return keyId.getKeyIdentifier();
        } else {
            return null;
        }
    }

    private byte[] getAKId(final X509Certificate cert) throws IOException {
        final byte[] extValue = cert.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
        if (extValue != null) {
            final DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream(extValue))
                    .readObject());
            final AuthorityKeyIdentifier keyId = new AuthorityKeyIdentifier(
                    (ASN1Sequence) new ASN1InputStream(new ByteArrayInputStream(oct.getOctets())).readObject());
            return keyId.getKeyIdentifier();
        } else {
            return null;
        }
    }

    private static String getBasicConstraints(final X509Certificate cert) {
        final int x = cert.getBasicConstraints();
        return (x < 0) ? "CA=false"
                : ("CA=true; PathLen=" + ((x == Integer.MAX_VALUE) ? "unlimited" : String.valueOf(x)));
    }

    private static List<String> getCrlDistributionPoints(final X509Certificate cert) throws IOException {
        final byte[] extValue = cert.getExtensionValue(X509Extensions.CRLDistributionPoints.getId());
        if (extValue != null) {
            final ASN1InputStream oAsnInStream = new ASN1InputStream(new ByteArrayInputStream(extValue));
            final DERObject derObj = oAsnInStream.readObject();
            final DEROctetString dos = (DEROctetString) derObj;
            final byte[] val2 = dos.getOctets();
            final ASN1InputStream oAsnInStream2 = new ASN1InputStream(new ByteArrayInputStream(val2));
            final DERObject derObj2 = oAsnInStream2.readObject();
            return getDERValue(derObj2);
        } else {
            return Collections.emptyList();
        }
    }

    @SuppressWarnings("unchecked")
    private static List<String> getDERValue(final DERObject derObj) {
        if (derObj instanceof DERSequence) {
            final List<String> ret = new LinkedList<String>();
            final DERSequence seq = (DERSequence) derObj;
            final Enumeration<DERObject> enum1 = seq.getObjects();
            while (enum1.hasMoreElements()) {
                final DERObject nestedObj = (DERObject) enum1.nextElement();
                final List<String> appo = getDERValue(nestedObj);
                if (appo != null) {
                    ret.addAll(appo);
                }
            }
            return ret;
        }

        if (derObj instanceof DERTaggedObject) {
            final DERTaggedObject derTag = (DERTaggedObject) derObj;
            if (derTag.isExplicit() && !derTag.isEmpty()) {
                final DERObject nestedObj = derTag.getObject();
                return getDERValue(nestedObj);
            } else {
                final DEROctetString derOct = (DEROctetString) derTag.getObject();
                final String val = new String(derOct.getOctets());
                return Collections.singletonList(val);
            }
        }

        return null;
    }

    private static final String[] keyUsageLabels = new String[] { "digitalSignature", "nonRepudiation",
            "keyEncipherment", "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", "encipherOnly",
            "decipherOnly" };

    private static List<String> getKeyUsage(final X509Certificate cert) {
        final boolean[] keyUsage = cert.getKeyUsage();
        if (keyUsage != null) {
            final List<String> ret = new LinkedList<String>();
            for (int i = 0; i < keyUsage.length; ++i) {
                if (keyUsage[i]) {
                    if (i < keyUsageLabels.length) {
                        ret.add(keyUsageLabels[i]);
                    } else {
                        ret.add(String.valueOf(i));
                    }
                }
            }
            return ret;
        } else {
            return null;
        }
    }

    protected PdfPTable createInfoTable() {
        final float alpha = 0.22f;
        final PdfPTable t = new PdfPTable(new float[] { alpha, 1.0f - alpha });
        t.getDefaultCell().setBorder(BORDER);
        t.setWidthPercentage(101f);
        return t;
    }

    protected void addItemRow(final String label, final String value, final PdfPTable table) {
        if (value != null) {
            table.addCell(new Phrase(label, labelFont));
            table.addCell(new Phrase(value, valueFont));
        }
    }

    protected void addItemRow(final String label, final Iterable<String> values, final PdfPTable table) {
        if (values != null) {
            boolean nonEmpty = false;
            final PdfPCell valueCell = new PdfPCell();
            valueCell.setBorder(0);
            for (String s : values) {
                valueCell.addElement(new Paragraph(s, valueFont));
                nonEmpty = true;
            }
            if (nonEmpty) {
                table.addCell(new Phrase(label, labelFont));
                table.addCell(valueCell);
            }
        }
    }

    protected void addLongItem(final String label, final String value, final Document doc)
            throws DocumentException {
        doc.add(new Paragraph(label, labelFont));
        doc.add(new Paragraph(value, valueFont));
    }

    protected void addLongMonoItem(final String label, final String value, final Document doc)
            throws DocumentException {
        doc.add(new Paragraph(label, labelFont));
        doc.add(new Paragraph(value, monoFont));
    }

    protected void addTitle(final String titleText, final Font titleFont, final int align,
            final float spacingBefore, final float spacingAfter, final Document doc) throws DocumentException {
        final Paragraph titlePara = new Paragraph(titleText, titleFont);
        titlePara.setAlignment(align);
        titlePara.setSpacingBefore(spacingBefore);
        titlePara.setSpacingAfter(spacingAfter);
        doc.add(titlePara);
    }

    protected static String substringAfter(final String mainString, final String substring) {
        return mainString.substring(mainString.indexOf(substring) + substring.length());
    }

    protected static String toHex(final byte[] value) {
        return (value != null) ? Hex.encodeHexString(value) : null;
    }

    private static File createTempDirectory() throws IOException {
        final File tmpDir = File.createTempFile("eid-tsl-", ".fonts");
        tmpDir.delete();
        tmpDir.mkdir();
        tmpDir.deleteOnExit();
        return tmpDir;
    }

}