/************************************************************************* * * * EJBCA Community: The OpenSource Certificate Authority * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.ejbca.ra; import java.io.Serializable; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.ViewScoped; import org.apache.commons.lang.StringUtils; import org.bouncycastle.util.encoders.Base64; import org.cesecore.certificates.ca.CAInfo; import org.cesecore.certificates.crl.CRLInfo; import org.cesecore.certificates.crl.CrlStoreSessionLocal; import org.cesecore.util.CertTools; import org.ejbca.config.WebConfiguration; import org.ejbca.core.model.era.RaMasterApiProxyBeanLocal; /** * Backing bean for Certificate and CRLs download page. * * @version $Id: RaCasPageBean.java 28756 2018-04-20 05:17:42Z bastianf $ */ @ManagedBean @ViewScoped public class RaCasPageBean implements Serializable { /** Representation of a CA in a chain with links to CRL download locations. */ public class CaAndCrl { private final String name; private final String subjectDn; private final int caId; private String crlLink; private String deltaCrlLink; private final int position; private final List chainNames; private boolean x509 = false; CaAndCrl(final String name, final String subjectDn, final int caId, final int position, final List chainNames) { this.name = name; this.subjectDn = subjectDn; this.caId = caId; this.position = position; this.chainNames = chainNames; } public String getName() { return name; } public String getSubjectDn() { return subjectDn; } public int getCaId() { return caId; } public String getCrlLink() { return crlLink; } public String getDeltaCrlLink() { return deltaCrlLink; } public int getPosition() { return position; } public boolean isX509() { return x509; } @Override public int hashCode() { return subjectDn.hashCode(); } @Override public boolean equals(final Object obj) { return obj instanceof CaAndCrl && subjectDn.equals(((CaAndCrl)obj).subjectDn); } /** @return the Subject DN string of the current certificate in unescaped RDN format */ public final String getSubjectDnUnescapedRndValue() { if (StringUtils.isNotEmpty(subjectDn)) { return org.ietf.ldap.LDAPDN.unescapeRDN(subjectDn); } else { return subjectDn; } } } private static final long serialVersionUID = 1L; //private static final Logger log = Logger.getLogger(RaCasPageBean.class); private static final String RFC4387_DEFAULT_EJBCA_URL = WebConfiguration.getCrlStoreContextRoot() + "/search.cgi"; private static final int NO_CAID_AVAILABLE = 0; @EJB private CrlStoreSessionLocal crlSession; @EJB private RaMasterApiProxyBeanLocal raMasterApiProxyBean; @ManagedProperty(value="#{raAuthenticationBean}") private RaAuthenticationBean raAuthenticationBean; public void setRaAuthenticationBean(final RaAuthenticationBean raAuthenticationBean) { this.raAuthenticationBean = raAuthenticationBean; } @ManagedProperty(value="#{raLocaleBean}") private RaLocaleBean raLocaleBean; public void setRaLocaleBean(final RaLocaleBean raLocaleBean) { this.raLocaleBean = raLocaleBean; } private List casAndCrlItems = null; private boolean atLeastOneCrlLinkPresent = false; /** @return true if at least one of the CAs available via #getCasAndCrlItems() has CRLs present on this system. */ public boolean isAtLeastOneCrlLinkPresent() { getCasAndCrlItems(); return atLeastOneCrlLinkPresent && WebConfiguration.isCrlStoreEnabled(); } /** @return a list of all known authorized CAs with links to CRLs (if present) */ public List getCasAndCrlItems() { if (casAndCrlItems==null) { final List caInfos = new ArrayList<>(raMasterApiProxyBean.getAuthorizedCas(raAuthenticationBean.getAuthenticationToken())); // First build a mapping of all subjects and their short names final Map caSubjectToNameMap = new HashMap<>(); for (final CAInfo caInfo : caInfos) { caSubjectToNameMap.put(caInfo.getSubjectDN(), caInfo.getName()); } // Convert all CA's chains into CA objects (use a Set to avoid duplicates) final Set cas = new HashSet<>(); for (final CAInfo caInfo : caInfos) { final List chain = new ArrayList<>(caInfo.getCertificateChain()); Collections.reverse(chain); final List chainNames = new ArrayList<>(); final int caId = caInfo.getCAId(); for (final Certificate caCertificate : chain) { final String subjectDn = CertTools.getSubjectDN(caCertificate); String name = caSubjectToNameMap.get(subjectDn); if (name==null) { name = subjectDn; } chainNames.add(name); final CaAndCrl caAndCrl = new CaAndCrl(name, subjectDn, chainNames.size()==chain.size()?caId:NO_CAID_AVAILABLE, chainNames.size()-1, new ArrayList<>(chainNames)); // Construct links to RFC4387 CRL Download Servlet if (caCertificate instanceof X509Certificate) { caAndCrl.x509 = true; final CRLInfo crlInfoFull = crlSession.getLastCRLInfo(subjectDn, false); if (crlInfoFull!=null) { atLeastOneCrlLinkPresent = true; caAndCrl.crlLink = RFC4387_DEFAULT_EJBCA_URL + "?iHash=" + getSubjectPrincipalHashAsUnpaddedBase64((X509Certificate)caCertificate); final CRLInfo crlInfoDelta = crlSession.getLastCRLInfo(subjectDn, true); if (crlInfoDelta!=null) { caAndCrl.deltaCrlLink = RFC4387_DEFAULT_EJBCA_URL + "?iHash=" + getSubjectPrincipalHashAsUnpaddedBase64((X509Certificate)caCertificate) + "&delta="; } } } // Add missing items and replace items when we know the CAId if (caAndCrl.getCaId()!=NO_CAID_AVAILABLE) { cas.remove(caAndCrl); } if (!cas.contains(caAndCrl)) { cas.add(caAndCrl); } } } casAndCrlItems = new ArrayList<>(cas); // Sort by higher level CAs Collections.sort(casAndCrlItems, new Comparator() { @Override public int compare(final CaAndCrl caAndCrl1, final CaAndCrl caAndCrl) { final int size1 = caAndCrl1.chainNames.size(); final int size2 = caAndCrl.chainNames.size(); // Avoid checking if chain length is the same for (int i=0; i