/**************************************************************************** * Copyright (C) 2012 ecsec GmbH. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.crypto.common.asn1.cvc; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements a chain of Card Verifiable Certificates. * See BSI-TR-03110, version 2.10, part 3, section 2. * See BSI-TR-03110, version 2.10, part 3, section C. * * @author Moritz Horsch <horsch@cdc.informatik.tu-darmstadt.de> */ public class CardVerifiableCertificateChain { private static final Logger _logger = LoggerFactory.getLogger(CertificateDescription.class); private ArrayList<CardVerifiableCertificate> certs = new ArrayList<CardVerifiableCertificate>(); private ArrayList<CardVerifiableCertificate> cvcaCerts = new ArrayList<CardVerifiableCertificate>(); private ArrayList<CardVerifiableCertificate> dvCerts = new ArrayList<CardVerifiableCertificate>(); private ArrayList<CardVerifiableCertificate> terminalCerts = new ArrayList<CardVerifiableCertificate>(); /** * Creates a new certificate chain. * * @param certificates Certificates * @throws CertificateException */ public CardVerifiableCertificateChain(List<CardVerifiableCertificate> certificates) throws CertificateException { parseChain(certificates); // FIXME not working yet with all servers. // verify(); _logger.warn("Verification of the certificate chain is disabled."); } /** * Parses the certificate chain. * * @param certificates Certificates */ private void parseChain(List<CardVerifiableCertificate> certificates) throws CertificateException { for (CardVerifiableCertificate cvc : certificates) { if (containsChertificate(cvc)) { break; } CHAT.Role role = cvc.getCHAT().getRole(); if (role.equals(CHAT.Role.CVCA)) { cvcaCerts.add(cvc); certs.add(cvc); } else if (role.equals(CHAT.Role.DV_OFFICIAL) || role.equals(CHAT.Role.DV_NON_OFFICIAL)) { dvCerts.add(cvc); certs.add(cvc); } else if (role.equals(CHAT.Role.AUTHENTICATION_TERMINAL) || role.equals(CHAT.Role.INSPECTION_TERMINAL) || role.equals(CHAT.Role.SIGNATURE_TERMINAL)) { terminalCerts.add(cvc); certs.add(cvc); } else { throw new CertificateException("Malformed certificate."); } } } /** * Verifies the certificate chain. * [1] The CAR and the CHR of the CVCA certificates should be equal. * [2] The CAR of a DV certificate should refer to the CHR of a CVCA certificate. * [3] The CAR of a terminal certificate should refer to the CHR of a DV certificate. * * @throws CertificateException */ private void verify() throws CertificateException { verify(terminalCerts, dvCerts); verify(dvCerts, cvcaCerts); verify(cvcaCerts, cvcaCerts); } private void verify(List<CardVerifiableCertificate> authorities, List<CardVerifiableCertificate> holders) throws CertificateException { for (Iterator<CardVerifiableCertificate> ai = authorities.iterator(); ai.hasNext();) { CardVerifiableCertificate authority = ai.next(); for (Iterator<CardVerifiableCertificate> hi = holders.iterator(); hi.hasNext();) { CardVerifiableCertificate holder = hi.next(); if (authority.getCAR().equals(holder.getCHR())) { break; } if (!ai.hasNext()) { throw new CertificateException( "Malformed certificate chain: Cannot find a CHR for the CAR (" + authority.getCAR().toString() + ")."); } } } } /** * Checks if the certificate chain contains the given certificate. * * @param cvc Certificate * @return True if the chain contains the certificate, false otherwise */ public boolean containsChertificate(CardVerifiableCertificate cvc) { for (CardVerifiableCertificate c : certs) { if (c.compare(cvc)) { return true; } } return false; } /** * Adds a new certificate to the chain. * * @param certificate Certificate * @throws CertificateException */ public void addCertificate(final CardVerifiableCertificate certificate) throws CertificateException { parseChain(new LinkedList<CardVerifiableCertificate>() { { add(certificate); } }); } /** * Adds new certificates to the chain. * * @param certificates Certificate * @throws CertificateException */ public void addCertificates(ArrayList<CardVerifiableCertificate> certificates) throws CertificateException { parseChain(certificates); } /** * Returns the certificates of the Country Verifying CAs (CVCA). * * @return CVCA certificates */ public List<CardVerifiableCertificate> getCVCACertificates() { return cvcaCerts; } /** * Returns the certificates of the Document Verifiers (DV). * * @return DV certificates */ public List<CardVerifiableCertificate> getDVCertificates() { return dvCerts; } /** * Returns the certificates of the terminal. * * @return Terminal certificates */ public List<CardVerifiableCertificate> getTerminalCertificates() { return terminalCerts; } /** * Returns the certificate chain. * * @return Certificate chain */ public List<CardVerifiableCertificate> getCertificates() { return certs; } /** * Returns the certificate chain from the CAR. * * @param car Certification Authority Reference (CAR) * @return Certificate chain * @throws CertificateException */ public CardVerifiableCertificateChain getCertificateChainFromCAR(byte[] car) throws CertificateException { return getCertificateChainFromCAR(new PublicKeyReference(car)); } /** * Returns the certificate chain from the CAR. * * @param car Certification Authority Reference (CAR) * @return Certificate chain * @throws CertificateException */ public CardVerifiableCertificateChain getCertificateChainFromCAR(PublicKeyReference car) throws CertificateException { List<CardVerifiableCertificate> certChain = buildChain(certs, car); return new CardVerifiableCertificateChain(certChain); } private ArrayList<CardVerifiableCertificate> buildChain(ArrayList<CardVerifiableCertificate> certs, PublicKeyReference car) { ArrayList<CardVerifiableCertificate> certChain = new ArrayList<CardVerifiableCertificate>(); for (CardVerifiableCertificate c : certs) { if (c.getCAR().compare(car)) { certChain.add(c); certChain.addAll(buildChain(certs, c.getCHR())); } } return certChain; } }