/* * * This file is part of the iText (R) project. Copyright (c) 1998-2017 iText Group NV * Authors: Bruno Lowagie, Paulo Soares, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS * * This program 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 Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA, 02110-1301 USA, or download the license from the following URL: * http://itextpdf.com/terms-of-use/ * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * In accordance with Section 7(b) of the GNU Affero General Public License, * a covered work must retain the producer line in every PDF that is created * or manipulated using iText. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the iText software without * disclosing the source code of your own applications. * These activities include: offering paid services to customers as an ASP, * serving PDFs on the fly in a web application, shipping iText with a closed * source product. * * For more information, please contact iText Software Corp. at this * address: sales@itextpdf.com */ package com.itextpdf.text.pdf.security; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.DistributionPoint; import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; /** * This class contains a series of static methods that * allow you to retrieve information from a Certificate. */ public class CertificateUtil { // Certificate Revocation Lists /** * Gets a CRL from a certificate * @param certificate * @return the CRL or null if there's no CRL available * @throws CertificateException * @throws CRLException * @throws IOException */ public static CRL getCRL(X509Certificate certificate) throws CertificateException, CRLException, IOException { return CertificateUtil.getCRL(CertificateUtil.getCRLURL(certificate)); } /** * Gets the URL of the Certificate Revocation List for a Certificate * @param certificate the Certificate * @return the String where you can check if the certificate was revoked * @throws CertificateParsingException * @throws IOException */ public static String getCRLURL(X509Certificate certificate) throws CertificateParsingException { ASN1Primitive obj; try { obj = getExtensionValue(certificate, Extension.cRLDistributionPoints.getId()); } catch (IOException e) { obj = null; } if (obj == null) { return null; } CRLDistPoint dist = CRLDistPoint.getInstance(obj); DistributionPoint[] dists = dist.getDistributionPoints(); for (DistributionPoint p : dists) { DistributionPointName distributionPointName = p.getDistributionPoint(); if (DistributionPointName.FULL_NAME != distributionPointName.getType()) { continue; } GeneralNames generalNames = (GeneralNames)distributionPointName.getName(); GeneralName[] names = generalNames.getNames(); for (GeneralName name : names) { if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { continue; } DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject)name.toASN1Primitive(), false); return derStr.getString(); } } return null; } /** * Gets the CRL object using a CRL URL. * @param url the URL where to get the CRL * @return a CRL object * @throws IOException * @throws CertificateException * @throws CRLException */ public static CRL getCRL(String url) throws IOException, CertificateException, CRLException { if (url == null) return null; InputStream is = new URL(url).openStream(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); return (CRL)cf.generateCRL(is); } // Online Certificate Status Protocol /** * Retrieves the OCSP URL from the given certificate. * @param certificate the certificate * @return the URL or null * @throws IOException */ public static String getOCSPURL(X509Certificate certificate) { ASN1Primitive obj; try { obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId()); if (obj == null) { return null; } ASN1Sequence AccessDescriptions = (ASN1Sequence) obj; for (int i = 0; i < AccessDescriptions.size(); i++) { ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i); if ( AccessDescription.size() != 2 ) { continue; } else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) { ASN1ObjectIdentifier id = (ASN1ObjectIdentifier)AccessDescription.getObjectAt(0); if (SecurityIDs.ID_OCSP.equals(id.getId())) { ASN1Primitive description = (ASN1Primitive)AccessDescription.getObjectAt(1); String AccessLocation = getStringFromGeneralName(description); if (AccessLocation == null) { return "" ; } else { return AccessLocation ; } } } } } catch (IOException e) { return null; } return null; } // Time Stamp Authority /** * Gets the URL of the TSA if it's available on the certificate * @param certificate a certificate * @return a TSA URL * @throws IOException */ public static String getTSAURL(X509Certificate certificate) { byte der[] = certificate.getExtensionValue(SecurityIDs.ID_TSA); if(der == null) return null; ASN1Primitive asn1obj; try { asn1obj = ASN1Primitive.fromByteArray(der); DEROctetString octets = (DEROctetString)asn1obj; asn1obj = ASN1Primitive.fromByteArray(octets.getOctets()); ASN1Sequence asn1seq = ASN1Sequence.getInstance(asn1obj); return getStringFromGeneralName(asn1seq.getObjectAt(1).toASN1Primitive()); } catch (IOException e) { return null; } } // helper methods /** * @param certificate the certificate from which we need the ExtensionValue * @param oid the Object Identifier value for the extension. * @return the extension value as an ASN1Primitive object * @throws IOException */ private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException { byte[] bytes = certificate.getExtensionValue(oid); if (bytes == null) { return null; } ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); return aIn.readObject(); } /** * Gets a String from an ASN1Primitive * @param names the ASN1Primitive * @return a human-readable String * @throws IOException */ private static String getStringFromGeneralName(ASN1Primitive names) throws IOException { ASN1TaggedObject taggedObject = (ASN1TaggedObject) names ; return new String(ASN1OctetString.getInstance(taggedObject, false).getOctets(), "ISO-8859-1"); } }