Skip to content

Commits on Source 4

Release Notes -- Apache PDFBox -- Version 2.0.12
Release Notes -- Apache PDFBox -- Version 2.0.13
Introduction
------------
The Apache PDFBox library is an open source Java tool for working with PDF documents.
This is an incremental bugfix release based on the earlier 2.0.11 release. It contains
This is an incremental bugfix release based on the earlier 2.0.12 release. It contains
a couple of fixes and small improvements.
For more details on these changes and all the other fixes and improvements
......@@ -14,52 +14,59 @@ PDFBox issue tracker at https://issues.apache.org/jira/browse/PDFBOX.
Bug
[PDFBOX-4013] - Java 9/macOS: Debugger App does not start (NoSuchMethodException)
[PDFBOX-4219] - Multithreading problem when rendering several documents with Standard 14 fonts
[PDFBOX-4242] - Fontbox does not close file descriptor when loading fonts.
[PDFBOX-4245] - wrong rendering of the transparency group at the specific position on a page
[PDFBOX-4254] - PDDocument.close() might ignore throwing an Exception
[PDFBOX-4266] - Java 6 error
[PDFBOX-4267] - Incorrect rendering when /Matte entry
[PDFBOX-4268] - Japanese text displayed as barcode
[PDFBOX-4276] - Multiply blend mode not detected
[PDFBOX-4278] - Type 3 font .notdef-named glyph missing in rendering
[PDFBOX-4279] - ArrayIndexOutOfBoundsException in PDDeviceGray.toRGB
[PDFBOX-4283] - Allowing Rectangles with additional elements
[PDFBOX-4288] - needless adding while parsing dictionary objects
[PDFBOX-4291] - JavaDoc on website is marked as being in German
[PDFBOX-4292] - Validation fails if ModifyDate and ModDate are specified using different time zones
[PDFBOX-4298] - NullPointerException when doing overlay
[PDFBOX-4299] - ArrayIndexOutOfBoundsException in CmapSubtable.processSubtype2
[PDFBOX-4301] - ClassCastException in PDExtendedGraphicsState
[PDFBOX-4302] - ToUnicode CMap is not written correctly when the entry count is just 100.
[PDFBOX-4305] - Log message not in english
[PDFBOX-4306] - Image clipping area rounding error
[PDFBOX-4307] - ClassCastException in PDDocumentCatalog.getDocumentOutline if 'outlines' is not a dictionary
[PDFBOX-4308] - PDDocument protect changes color palette
[PDFBOX-4315] - PDFBox info message when not using it
[PDFBOX-4316] - RemoveAllText does not delete all parameters with " operator
[PDFBOX-4318] - PDFont.encode results change on identical input
[PDFBOX-4319] - Parsing 100000 page pdf is slow
[PDFBOX-4322] - Extract Text feature is not working for some part of PDF
[PDFBOX-3646] - Annotations parsed from XFDF containing ampersand characters are not properly imported
[PDFBOX-4163] - Java 11 compile error
[PDFBOX-4326] - PDF with JPEG2000 image can't be rendered
[PDFBOX-4327] - NullPointerException in PDFStreamEngine.processSoftMask() when running ExtractImages
[PDFBOX-4330] - NumberFormatException in CFFParser.readRealNumber()
[PDFBOX-4331] - Make jdk9 profile activation automatic
[PDFBOX-4333] - ClassCastException when loading PDF
[PDFBOX-4336] - "CMap is invalid" exception thrown
[PDFBOX-4338] - ArrayIndexOutOfBoundsException in COSParser
[PDFBOX-4339] - NullPointerException in COSParser
[PDFBOX-4343] - Prevent calling addSignature twice
[PDFBOX-4345] - FDFAnnotation.richContentsToString does not evaluate text nodes which have siblings in the XML
[PDFBOX-4347] - ArrayIndexOutOfBoundsException in PDFXrefStreamParser
[PDFBOX-4348] - ClassCastException in COSParser
[PDFBOX-4349] - ClassCastException in COSParser
[PDFBOX-4350] - IllegalArgumentException in PDFObjectStreamParser
[PDFBOX-4351] - IndexOutOfBoundsException when reading from InputStreamSource
[PDFBOX-4352] - NullPointerException in COSParser
[PDFBOX-4353] - NullPointerException in PDFXrefStreamParser
[PDFBOX-4354] - NumberFormatException in COSParser
[PDFBOX-4355] - PDFTextStripperByArea dies on Chinese/Japanese files
[PDFBOX-4357] - IllegalArgumentException "root cannot be null"
[PDFBOX-4359] - Bad sizing of signature field inside rotated page
[PDFBOX-4360] - ArrayIndexOutOfBoundsException in ASCIIHexFilter
[PDFBOX-4361] - ArrayIndexOutOfBoundsException in COSParser
[PDFBOX-4364] - example AddValidationInformation fails with scratchfile error
[PDFBOX-4365] - PDFDebugger: JComboBox does not take generic parameters in Java 1.6
[PDFBOX-4366] - NullPointerException in PDButton.updateByValue() when appearance missing
[PDFBOX-4367] - Error expected floating point number actual='18-5'
[PDFBOX-4369] - unsupported ExtractText -force option still appears in online 2.0 docs
[PDFBOX-4372] - Stack overflow around PDFStreamEngine.processStream
[PDFBOX-4374] - Switch from log4j to slf4j
[PDFBOX-4377] - Verify CRL in AddValidation example
[PDFBOX-4381] - Revocation CRL check should be done at signing time in AddValidation example
[PDFBOX-4383] - PDFMergerUtility seems to leave source file open
[PDFBOX-4384] - PDF/A Document Validation out of memory
Improvement
[PDFBOX-4184] - [PATCH]: Support simple lossless compression of 16 bit RGB images
[PDFBOX-4253] - Optimize PDFunctionType3.eval()
[PDFBOX-4256] - Return default value for CheckBox / RadioButton if /V entry is missing (widget /AS entries are present)
[PDFBOX-4259] - Add polygon annotation to AddAnnotations.java example
[PDFBOX-4260] - Reduce RAM requirement of COSOutputStream
[PDFBOX-4263] - Check object key when signing
[PDFBOX-4271] - Consistently using the same version of the download-maven-plugin
[PDFBOX-4274] - Get rid of warning about prerequisites
[PDFBOX-4285] - Expose the tiff compression type to the user.
[PDFBOX-4289] - Double negative number
[PDFBOX-4295] - Don't create intermediate streams when merging files
[PDFBOX-4335] - Overlay should implement Closeable
[PDFBOX-4363] - [Patch] Add a common interface PDShadingPaint for all shading paints
[PDFBOX-4371] - Improve ExtractText utility so that it can extract rotated text automatically
[PDFBOX-4375] - Change visibility of Overlay#loadPDF to protected
Test
[PDFBOX-4373] - Add additional unit tests
Task
[PDFBOX-4281] - Replace Apache Wink dependency
[PDFBOX-4358] - Prevent stack overflow in COSDictionary.toString()
[PDFBOX-4362] - Create simple text extraction example
Release Contents
----------------
......
......@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-parent</artifactId>
<version>2.0.12</version>
<version>2.0.13</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
......@@ -68,7 +68,7 @@
<Embed-Transitive>true</Embed-Transitive>
<Embed-Dependency>*;scope=provided;inline=org/apache/**|org/bouncycastle/**|META-INF/services/**</Embed-Dependency>
<Bundle-DocURL>${project.url}</Bundle-DocURL>
<Import-Package>!junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,org.apache.log4j;resolution:=optional,*</Import-Package>
<Import-Package>!junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,*</Import-Package>
<Main-Class>org.apache.pdfbox.tools.PDFBox</Main-Class>
</instructions>
</configuration>
......
libpdfbox2-java (2.0.13-1) unstable; urgency=medium
* New upstream version 2.0.13.
-- Markus Koschany <apo@debian.org> Sat, 08 Dec 2018 15:50:15 +0100
libpdfbox2-java (2.0.12-1) unstable; urgency=medium
* New upstream version 2.0.12.
......
......@@ -9,4 +9,4 @@ dversionmangle=s/(?:\.|\+)dfsg$//,\
downloadurlmangle=s/pdfbox\/([\d.]+)\//pdfbox\/$1\/pdfbox-$1-src.zip/,\
filenamemangle=s/([\d.]+)\//pdfbox-$1-src.zip/,\
pgpsigurlmangle=s/$/.asc/ \
http://archive.apache.org/dist/pdfbox/ ([\d.]+)/
https://archive.apache.org/dist/pdfbox/ ([\d.]+)/
......@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-parent</artifactId>
<version>2.0.12</version>
<version>2.0.13</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
......@@ -68,7 +68,7 @@
<Embed-Transitive>true</Embed-Transitive>
<Embed-Dependency>*;scope=provided;inline=org/apache/**|org/bouncycastle/**|META-INF/services/**</Embed-Dependency>
<Bundle-DocURL>${project.url}</Bundle-DocURL>
<Import-Package>!junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,org.apache.log4j;resolution:=optional,*</Import-Package>
<Import-Package>!junit.framework,!junit.textui,javax.*;resolution:=optional,org.apache.avalon.framework.logger;resolution:=optional,org.apache.log;resolution:=optional,*</Import-Package>
<Main-Class>org.apache.pdfbox.debugger.PDFDebugger</Main-Class>
</instructions>
</configuration>
......
......@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-parent</artifactId>
<version>2.0.12</version>
<version>2.0.13</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
......
......@@ -906,7 +906,9 @@ public class PDFDebugger extends JFrame
{
selectedNode = ((MapEntry)selectedNode).getKey();
selectedNode = getUnderneathObject(selectedNode);
FlagBitsPane flagBitsPane = new FlagBitsPane((COSDictionary) parentNode, (COSName) selectedNode);
FlagBitsPane flagBitsPane = new FlagBitsPane(document,
(COSDictionary) parentNode,
(COSName) selectedNode);
replaceRightComponent(flagBitsPane.getPane());
}
}
......
......@@ -20,6 +20,7 @@ package org.apache.pdfbox.debugger.flagbitspane;
import javax.swing.JPanel;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
/**
* @author Khyrul Bashar
......@@ -29,14 +30,16 @@ import org.apache.pdfbox.cos.COSName;
public class FlagBitsPane
{
private FlagBitsPaneView view;
private final PDDocument document;
/**
* Constructor.
* @param dictionary COSDictionary instance.
* @param flagType COSName instance.
*/
public FlagBitsPane(final COSDictionary dictionary, COSName flagType)
public FlagBitsPane(PDDocument document, final COSDictionary dictionary, COSName flagType)
{
this.document = document;
createPane(dictionary, flagType);
}
......@@ -79,7 +82,7 @@ public class FlagBitsPane
}
if (COSName.SIG_FLAGS.equals(flagType))
{
flag = new SigFlag(dictionary);
flag = new SigFlag(document, dictionary);
view = new FlagBitsPaneView(
flag.getFlagType(), flag.getFlagValue(), flag.getFlagBits(), flag.getColumnNames());
}
......
......@@ -19,6 +19,7 @@ package org.apache.pdfbox.debugger.flagbitspane;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
/**
......@@ -28,16 +29,18 @@ import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
*/
public class SigFlag extends Flag
{
private final COSDictionary acroformDictionary;
private final PDDocument document;
private final COSDictionary acroFormDictionary;
/**
* Constructor
*
* @param acroFormDictionary COSDictionary instance.
*/
SigFlag(COSDictionary acroFormDictionary)
SigFlag(PDDocument document, COSDictionary acroFormDictionary)
{
acroformDictionary = acroFormDictionary;
this.document = document;
this.acroFormDictionary = acroFormDictionary;
}
@Override
......@@ -49,13 +52,13 @@ public class SigFlag extends Flag
@Override
String getFlagValue()
{
return "Flag value: " + acroformDictionary.getInt(COSName.SIG_FLAGS);
return "Flag value: " + acroFormDictionary.getInt(COSName.SIG_FLAGS);
}
@Override
Object[][] getFlagBits()
{
PDAcroForm acroForm = new PDAcroForm(null, acroformDictionary);
PDAcroForm acroForm = new PDAcroForm(document, acroFormDictionary);
return new Object[][]{
new Object[]{1, "SignaturesExist", acroForm.isSignaturesExist()},
new Object[]{2, "AppendOnly", acroForm.isAppendOnly()},
......
......@@ -165,7 +165,7 @@ public class StreamPane implements ActionListener
private JPanel createHeaderPanel(List<String> availableFilters, String i, ActionListener actionListener)
{
JComboBox<String> filters = new JComboBox<String>(new Vector<String>(availableFilters));
JComboBox filters = new JComboBox(new Vector<String>(availableFilters));
filters.setSelectedItem(i);
filters.addActionListener(actionListener);
......
......@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-parent</artifactId>
<version>2.0.12</version>
<version>2.0.13</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
......@@ -42,6 +42,26 @@
<!-- don't update this, because later versions require JDK7 -->
</properties>
<profiles>
<profile>
<activation>
<jdk>[11,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
......
......@@ -21,6 +21,7 @@ import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PageMode;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageFitWidthDestination;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
......@@ -85,6 +86,9 @@ public final class CreateBookmarks
pagesOutline.openNode();
outline.openNode();
// optional: show the outlines when opening the file
document.getDocumentCatalog().setPageMode(PageMode.USE_OUTLINES);
document.save( args[1] );
}
finally
......
......@@ -27,12 +27,9 @@ import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
......@@ -42,7 +39,6 @@ import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public abstract class CreateSignatureBase implements SignatureInterface
{
......@@ -87,6 +83,8 @@ public abstract class CreateSignatureBase implements SignatureInterface
{
// avoid expired certificate
((X509Certificate) cert).checkValidity();
SigUtils.checkCertificateUsage((X509Certificate) cert);
}
break;
}
......@@ -130,14 +128,11 @@ public abstract class CreateSignatureBase implements SignatureInterface
// cannot be done private (interface)
try
{
List<Certificate> certList = new ArrayList<Certificate>();
certList.addAll(Arrays.asList(certificateChain));
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(certificateChain[0].getEncoded());
X509Certificate cert = (X509Certificate) certificateChain[0];
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(cert)));
gen.addCertificates(certs);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, cert));
gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
CMSSignedData signedData = gen.generate(msg, false);
if (tsaUrl != null && tsaUrl.length() > 0)
......
......@@ -16,12 +16,21 @@
package org.apache.pdfbox.examples.signature;
import java.io.IOException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.asn1.x509.KeyPurposeId;
/**
* Utility class for the signature / timestamp examples.
......@@ -30,6 +39,8 @@ import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
*/
public class SigUtils
{
private static final Log LOG = LogFactory.getLog(SigUtils.class);
private SigUtils()
{
}
......@@ -126,4 +137,103 @@ public class SigUtils
catalogDict.setNeedToBeUpdated(true);
permsDict.setNeedToBeUpdated(true);
}
/**
* Log if the certificate is not valid for signature usage. Doing this
* anyway results in Adobe Reader failing to validate the PDF.
*
* @param x509Certificate
* @throws java.security.cert.CertificateParsingException
*/
public static void checkCertificateUsage(X509Certificate x509Certificate)
throws CertificateParsingException
{
// Check whether signer certificate is "valid for usage"
// https://stackoverflow.com/a/52765021/535646
// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html#id1
boolean[] keyUsage = x509Certificate.getKeyUsage();
if (keyUsage != null && !keyUsage[0] && !keyUsage[1])
{
// (unclear what "signTransaction" is)
// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
LOG.error("Certificate key usage does not include " +
"digitalSignature nor nonRepudiation");
}
List<String> extendedKeyUsage = x509Certificate.getExtendedKeyUsage();
if (extendedKeyUsage != null &&
!extendedKeyUsage.contains(KeyPurposeId.id_kp_emailProtection.toString()) &&
!extendedKeyUsage.contains(KeyPurposeId.id_kp_codeSigning.toString()) &&
!extendedKeyUsage.contains(KeyPurposeId.anyExtendedKeyUsage.toString()) &&
!extendedKeyUsage.contains("1.2.840.113583.1.1.5") &&
// not mentioned in Adobe document, but tolerated in practice
!extendedKeyUsage.contains("1.3.6.1.4.1.311.10.3.12"))
{
LOG.error("Certificate extended key usage does not include " +
"emailProtection, nor codeSigning, nor anyExtendedKeyUsage, " +
"nor 'Adobe Authentic Documents Trust'");
}
}
/**
* Log if the certificate is not valid for timestamping.
*
* @param x509Certificate
* @throws java.security.cert.CertificateParsingException
*/
public static void checkTimeStampCertificateUsage(X509Certificate x509Certificate)
throws CertificateParsingException
{
List<String> extendedKeyUsage = x509Certificate.getExtendedKeyUsage();
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
if (extendedKeyUsage != null &&
!extendedKeyUsage.contains(KeyPurposeId.id_kp_timeStamping.toString()))
{
LOG.error("Certificate extended key usage does not include timeStamping");
}
}
/**
* Log if the certificate is not valid for responding.
*
* @param x509Certificate
* @throws java.security.cert.CertificateParsingException
*/
public static void checkResponderCertificateUsage(X509Certificate x509Certificate)
throws CertificateParsingException
{
List<String> extendedKeyUsage = x509Certificate.getExtendedKeyUsage();
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
if (extendedKeyUsage != null &&
!extendedKeyUsage.contains(KeyPurposeId.id_kp_OCSPSigning.toString()))
{
LOG.error("Certificate extended key usage does not include OCSP responding");
}
}
/**
* Gets the last relevant signature in the document, i.e. the one with the highest offset.
*
* @param document to get its last signature
* @return last signature or null when none found
* @throws IOException
*/
public static PDSignature getLastRelevantSignature(PDDocument document) throws IOException
{
SortedMap<Integer, PDSignature> sortedMap = new TreeMap<Integer, PDSignature>();
for (PDSignature signature : document.getSignatureDictionaries())
{
int sigOffset = signature.getByteRange()[1];
sortedMap.put(sigOffset, signature);
}
if (sortedMap.size() > 0)
{
PDSignature lastSignature = sortedMap.get(sortedMap.lastKey());
COSBase type = lastSignature.getCOSObject().getItem(COSName.TYPE);
if (type.equals(COSName.SIG) || type.equals(COSName.DOC_TIME_STAMP))
{
return lastSignature;
}
}
return null;
}
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pdfbox.examples.signature.cert;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLEntry;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
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;
/**
* Copied from Apache CXF 2.4.9, initial version:
* https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.9/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/
*
*/
public final class CRLVerifier
{
private static final Log LOG = LogFactory.getLog(CRLVerifier.class);
private CRLVerifier()
{
}
/**
* Extracts the CRL distribution points from the certificate (if available)
* and checks the certificate revocation status against the CRLs coming from
* the distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs.
*
* @param cert the certificate to be checked for revocation
* @param signDate the date when the signing took place
* @param additionalCerts set of trusted root CA certificates that will be
* used as "trust anchors" and intermediate CA certificates that will be
* used as part of the certification chain.
* @throws CertificateVerificationException if the certificate could not be verified
* @throws RevokedCertificateException if the certificate is revoked
*/
public static void verifyCertificateCRLs(X509Certificate cert, Date signDate,
Set<X509Certificate> additionalCerts)
throws CertificateVerificationException, RevokedCertificateException
{
try
{
Exception firstException = null;
List<String> crlDistributionPointsURLs = getCrlDistributionPoints(cert);
for (String crlDistributionPointsURL : crlDistributionPointsURLs)
{
LOG.info("Checking distribution point URL: " + crlDistributionPointsURL);
X509CRL crl;
try
{
crl = downloadCRL(crlDistributionPointsURL);
}
catch (Exception ex)
{
// e.g. LDAP behind corporate proxy
LOG.warn("Caught " + ex.getClass().getSimpleName() + " downloading CRL, will try next distribution point if available");
if (firstException == null)
{
firstException = ex;
}
continue;
}
// Verify CRL, see wikipedia:
// "To validate a specific CRL prior to relying on it,
// the certificate of its corresponding CA is needed"
PublicKey issuerKey = null;
for (X509Certificate additionalCert : additionalCerts)
{
if (crl.getIssuerX500Principal().equals(
additionalCert.getSubjectX500Principal()))
{
issuerKey = additionalCert.getPublicKey();
}
}
if (issuerKey == null)
{
throw new CertificateVerificationException(
"Certificate for " + crl.getIssuerX500Principal() +
"not found in certificate chain, so the CRL at " +
crlDistributionPointsURL + " could not be verified");
}
crl.verify(issuerKey, SecurityProvider.getProvider().getName());
checkRevocation(crl, cert, signDate, crlDistributionPointsURL);
// https://tools.ietf.org/html/rfc5280#section-4.2.1.13
// If the DistributionPointName contains multiple values,
// each name describes a different mechanism to obtain the same
// CRL. For example, the same CRL could be available for
// retrieval through both LDAP and HTTP.
//
// => thus no need to check several protocols
return;
}
if (firstException != null)
{
throw firstException;
}
}
catch (CertificateVerificationException ex)
{
throw ex;
}
catch (RevokedCertificateException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new CertificateVerificationException(
"Cannot verify CRL for certificate: "
+ cert.getSubjectX500Principal(), ex);
}
}
/**
* Check whether the certificate was revoked at signing time.
*
* @param crl certificate revocation list
* @param cert certificate to be checked
* @param signDate date the certificate was used for signing
* @param crlDistributionPointsURL URL for log message or exception text
* @throws RevokedCertificateException if the certificate was revoked at signing time
*/
public static void checkRevocation(
X509CRL crl, X509Certificate cert, Date signDate, String crlDistributionPointsURL)
throws RevokedCertificateException
{
X509CRLEntry revokedCRLEntry = crl.getRevokedCertificate(cert);
if (revokedCRLEntry != null &&
revokedCRLEntry.getRevocationDate().compareTo(signDate) <= 0)
{
throw new RevokedCertificateException(
"The certificate was revoked by CRL " +
crlDistributionPointsURL + " on " + revokedCRLEntry.getRevocationDate(),
revokedCRLEntry.getRevocationDate());
}
else if (revokedCRLEntry != null)
{
LOG.info("The certificate was revoked after signing by CRL " +
crlDistributionPointsURL + " on " + revokedCRLEntry.getRevocationDate());
}
else
{
LOG.info("The certificate was not revoked by CRL " + crlDistributionPointsURL);
}
}
/**
* Downloads CRL from given URL. Supports http, https, ftp and ldap based URLs.
*/
private static X509CRL downloadCRL(String crlURL) throws IOException,
CertificateException, CRLException,
CertificateVerificationException, NamingException
{
if (crlURL.startsWith("http://") || crlURL.startsWith("https://")
|| crlURL.startsWith("ftp://"))
{
return downloadCRLFromWeb(crlURL);
}
else if (crlURL.startsWith("ldap://"))
{
return downloadCRLFromLDAP(crlURL);
}
else
{
throw new CertificateVerificationException(
"Can not download CRL from certificate "
+ "distribution point: " + crlURL);
}
}
/**
* Downloads a CRL from given LDAP url, e.g.
* ldap://ldap.infonotary.com/dc=identity-ca,dc=infonotary,dc=com
*/
private static X509CRL downloadCRLFromLDAP(String ldapURL) throws CertificateException,
NamingException, CRLException,
CertificateVerificationException
{
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
// https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/create.html
// don't wait forever behind corporate proxy
env.put("com.sun.jndi.ldap.connect.timeout", "1000");
DirContext ctx = new InitialDirContext(env);
Attributes avals = ctx.getAttributes("");
Attribute aval = avals.get("certificateRevocationList;binary");
byte[] val = (byte[]) aval.get();
if (val == null || val.length == 0)
{
throw new CertificateVerificationException("Can not download CRL from: " + ldapURL);
}
else
{
InputStream inStream = new ByteArrayInputStream(val);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509CRL) cf.generateCRL(inStream);
}
}
/**
* Downloads a CRL from given HTTP/HTTPS/FTP URL, e.g.
* http://crl.infonotary.com/crl/identity-ca.crl
*/
public static X509CRL downloadCRLFromWeb(String crlURL)
throws IOException, CertificateException, CRLException
{
InputStream crlStream = new URL(crlURL).openStream();
try
{
return (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(crlStream);
}
finally
{
crlStream.close();
}
}
/**
* Extracts all CRL distribution point URLs from the "CRL Distribution
* Point" extension in a X.509 certificate. If CRL distribution point
* extension is unavailable, returns an empty list.
* @param cert
* @return List of CRL distribution point URLs.
* @throws java.io.IOException
*/
public static List<String> getCrlDistributionPoints(X509Certificate cert)
throws IOException
{
byte[] crldpExt = cert.getExtensionValue(Extension.cRLDistributionPoints.getId());
if (crldpExt == null)
{
return new ArrayList<String>();
}
ASN1InputStream oAsnInStream = new ASN1InputStream(new ByteArrayInputStream(crldpExt));
ASN1Primitive derObjCrlDP = oAsnInStream.readObject();
DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP;
byte[] crldpExtOctets = dosCrlDP.getOctets();
ASN1InputStream oAsnInStream2 = new ASN1InputStream(new ByteArrayInputStream(crldpExtOctets));
ASN1Primitive derObj2 = oAsnInStream2.readObject();
CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2);
List<String> crlUrls = new ArrayList<String>();
for (DistributionPoint dp : distPoint.getDistributionPoints())
{
DistributionPointName dpn = dp.getDistributionPoint();
// Look for URIs in fullName
if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME)
{
// Look for an URI
for (GeneralName genName : GeneralNames.getInstance(dpn.getName()).getNames())
{
if (genName.getTagNo() == GeneralName.uniformResourceIdentifier)
{
String url = DERIA5String.getInstance(genName.getName()).getString();
crlUrls.add(url);
}
}
}
}
return crlUrls;
}
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pdfbox.examples.signature.cert;
/**
* Copied from Apache CXF 2.4.9, initial version:
* https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.9/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/
*
*/
public class CertificateVerificationException extends Exception
{
private static final long serialVersionUID = 1L;
public CertificateVerificationException(String message, Throwable cause)
{
super(message, cause);
}
public CertificateVerificationException(String message)
{
super(message);
}
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pdfbox.examples.signature.cert;
import java.security.cert.PKIXCertPathBuilderResult;
/**
* Copied from Apache CXF 2.4.9, initial version:
* https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.9/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/
*
*/
public class CertificateVerificationResult
{
private boolean valid;
private PKIXCertPathBuilderResult result;
private Throwable exception;
/**
* Constructs a certificate verification result for valid certificate by
* given certification path.
*/
public CertificateVerificationResult(PKIXCertPathBuilderResult result)
{
this.valid = true;
this.result = result;
}
public CertificateVerificationResult(Throwable exception)
{
this.valid = false;
this.exception = exception;
}
public boolean isValid()
{
return valid;
}
public PKIXCertPathBuilderResult getResult()
{
return result;
}
public Throwable getException()
{
return exception;
}
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pdfbox.examples.signature.cert;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.encryption.SecurityProvider;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPResp;
/**
* Copied from Apache CXF 2.4.9, initial version:
* https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.9/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/
*
*/
public final class CertificateVerifier
{
private static final Log LOG = LogFactory.getLog(CertificateVerifier.class);
private CertificateVerifier()
{
}
/**
* Attempts to build a certification chain for given certificate and to
* verify it. Relies on a set of root CA certificates and intermediate
* certificates that will be used for building the certification chain. The
* verification process assumes that all self-signed certificates in the set
* are trusted root CA certificates and all other certificates in the set
* are intermediate certificates.
*
* @param cert - certificate for validation
* @param additionalCerts - set of trusted root CA certificates that will be
* used as "trust anchors" and intermediate CA certificates that will be
* used as part of the certification chain. All self-signed certificates are
* considered to be trusted root CA certificates. All the rest are
* considered to be intermediate CA certificates.
* @param verifySelfSignedCert true if a self-signed certificate is accepted, false if not.
* @param signDate the date when the signing took place
* @return the certification chain (if verification is successful)
* @throws CertificateVerificationException - if the certification is not
* successful (e.g. certification path cannot be built or some certificate
* in the chain is expired or CRL checks are failed)
*/
public static PKIXCertPathBuilderResult verifyCertificate(
X509Certificate cert, Set<X509Certificate> additionalCerts,
boolean verifySelfSignedCert, Date signDate)
throws CertificateVerificationException
{
try
{
// Check for self-signed certificate
if (!verifySelfSignedCert && isSelfSigned(cert))
{
throw new CertificateVerificationException("The certificate is self-signed.");
}
// Prepare a set of trust anchors (set of root CA certificates)
// and a set of intermediate certificates
Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
for (X509Certificate additionalCert : additionalCerts)
{
if (isSelfSigned(additionalCert))
{
trustAnchors.add(new TrustAnchor(additionalCert, null));
}
else
{
intermediateCerts.add(additionalCert);
}
}
if (trustAnchors.isEmpty())
{
throw new CertificateVerificationException("No root certificate in the chain");
}
// Attempt to build the certification chain and verify it
PKIXCertPathBuilderResult verifiedCertChain = verifyCertificate(
cert, trustAnchors, intermediateCerts, signDate);
LOG.info("Certification chain verified successfully");
checkRevocations(cert, additionalCerts, signDate);
return verifiedCertChain;
}
catch (CertPathBuilderException certPathEx)
{
throw new CertificateVerificationException(
"Error building certification path: "
+ cert.getSubjectX500Principal(), certPathEx);
}
catch (CertificateVerificationException cvex)
{
throw cvex;
}
catch (Exception ex)
{
throw new CertificateVerificationException(
"Error verifying the certificate: "
+ cert.getSubjectX500Principal(), ex);
}
}
private static void checkRevocations(X509Certificate cert,
Set<X509Certificate> additionalCerts,
Date signDate)
throws IOException, CertificateVerificationException, OCSPException,
RevokedCertificateException, GeneralSecurityException
{
if (isSelfSigned(cert))
{
// root, we're done
return;
}
X509Certificate issuerCert = null;
for (X509Certificate additionalCert : additionalCerts)
{
if (cert.getIssuerX500Principal().equals(additionalCert.getSubjectX500Principal()))
{
issuerCert = additionalCert;
break;
}
}
// issuerCert is never null here. If it hadn't been found, then there wouldn't be a
// verifiedCertChain earlier.
// Try checking the certificate through OCSP (faster than CRL)
String ocspURL = extractOCSPURL(cert);
if (ocspURL != null)
{
OcspHelper ocspHelper = new OcspHelper(cert, signDate, issuerCert, additionalCerts, ocspURL);
try
{
verifyOCSP(ocspHelper, additionalCerts);
}
catch (IOException ex)
{
// happens with 021496.pdf because OCSP responder no longer exists
LOG.warn("IOException trying OCSP, will try CRL", ex);
CRLVerifier.verifyCertificateCRLs(cert, signDate, additionalCerts);
}
}
else
{
LOG.info("OCSP not available, will try CRL");
// Check whether the certificate is revoked by the CRL
// given in its CRL distribution point extension
CRLVerifier.verifyCertificateCRLs(cert, signDate, additionalCerts);
}
// now check the issuer
checkRevocations(issuerCert, additionalCerts, signDate);
}
/**
* Checks whether given X.509 certificate is self-signed.
* @param cert The X.509 certificate to check.
* @return true if the certificate is self-signed, false if not.
* @throws java.security.GeneralSecurityException
*/
public static boolean isSelfSigned(X509Certificate cert) throws GeneralSecurityException
{
try
{
// Try to verify certificate signature with its own public key
PublicKey key = cert.getPublicKey();
cert.verify(key, SecurityProvider.getProvider().getName());
return true;
}
catch (SignatureException ex)
{
// Invalid signature --> not self-signed
LOG.debug("Couldn't get signature information - returning false", ex);
return false;
}
catch (InvalidKeyException ex)
{
// Invalid signature --> not self-signed
LOG.debug("Couldn't get signature information - returning false", ex);
return false;
}
catch (IOException ex)
{
// Invalid signature --> not self-signed
LOG.debug("Couldn't get signature information - returning false", ex);
return false;
}
}
/**
* Attempts to build a certification chain for given certificate and to
* verify it. Relies on a set of root CA certificates (trust anchors) and a
* set of intermediate certificates (to be used as part of the chain).
*
* @param cert - certificate for validation
* @param trustAnchors - set of trust anchors
* @param intermediateCerts - set of intermediate certificates
* @param signDate the date when the signing took place
* @return the certification chain (if verification is successful)
* @throws GeneralSecurityException - if the verification is not successful
* (e.g. certification path cannot be built or some certificate in the chain
* is expired)
*/
private static PKIXCertPathBuilderResult verifyCertificate(
X509Certificate cert, Set<TrustAnchor> trustAnchors,
Set<X509Certificate> intermediateCerts, Date signDate)
throws GeneralSecurityException
{
// Create the selector that specifies the starting certificate
X509CertSelector selector = new X509CertSelector();
selector.setCertificate(cert);
// Configure the PKIX certificate builder algorithm parameters
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
// Disable CRL checks (this is done manually as additional step)
pkixParams.setRevocationEnabled(false);
// not doing this brings
// "SunCertPathBuilderException: unable to find valid certification path to requested target"
// (when using -Djava.security.debug=certpath: "critical policy qualifiers present in certificate")
// for files like 021496.pdf that have the "Adobe CDS Certificate Policy" 1.2.840.113583.1.2.1
// CDS = "Certified Document Services"
// https://www.adobe.com/misc/pdfs/Adobe_CDS_CP.pdf
pkixParams.setPolicyQualifiersRejected(false);
// However, maybe there is still work to do:
// "If the policyQualifiersRejected flag is set to false, it is up to the application
// to validate all policy qualifiers in this manner in order to be PKIX compliant."
pkixParams.setDate(signDate);
// Specify a list of intermediate certificates
CertStore intermediateCertStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCerts));
pkixParams.addCertStore(intermediateCertStore);
// Build and verify the certification chain
// If this doesn't work although it should, it can be debugged
// by starting java with -Djava.security.debug=certpath
// see also
// https://docs.oracle.com/javase/8/docs/technotes/guides/security/troubleshooting-security.html
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
return (PKIXCertPathBuilderResult) builder.build(pkixParams);
}
/**
* Extract the OCSP URL from an X.509 certificate if available.
*
* @param cert X.509 certificate
* @return the URL of the OCSP validation service
* @throws IOException
*/
private static String extractOCSPURL(X509Certificate cert) throws IOException
{
byte[] authorityExtensionValue = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
if (authorityExtensionValue != null)
{
// copied from CertInformationHelper.getAuthorityInfoExtensionValue()
// DRY refactor should be done some day
ASN1Sequence asn1Seq = (ASN1Sequence) JcaX509ExtensionUtils.parseExtensionValue(authorityExtensionValue);
Enumeration<?> objects = asn1Seq.getObjects();
while (objects.hasMoreElements())
{
// AccessDescription
ASN1Sequence obj = (ASN1Sequence) objects.nextElement();
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) obj.getObjectAt(0);
// accessLocation
DERTaggedObject location = (DERTaggedObject) obj.getObjectAt(1);
if (oid.equals(X509ObjectIdentifiers.id_ad_ocsp)
&& location.getTagNo() == GeneralName.uniformResourceIdentifier)
{
DEROctetString url = (DEROctetString) location.getObject();
String ocspURL = new String(url.getOctets());
LOG.info("OCSP URL: " + ocspURL);
return ocspURL;
}
}
}
return null;
}
/**
* Verify whether the certificate has been revoked at signing date, and verify whether the
* certificate of the responder has been revoked now.
*
* @param ocspHelper the OCSP helper.
* @param additionalCerts
* @throws RevokedCertificateException
* @throws IOException
* @throws OCSPException
* @throws CertificateVerificationException
*/
private static void verifyOCSP(OcspHelper ocspHelper, Set<X509Certificate> additionalCerts)
throws RevokedCertificateException, IOException, OCSPException, CertificateVerificationException
{
Date now = Calendar.getInstance().getTime();
OCSPResp ocspResponse;
ocspResponse = ocspHelper.getResponseOcsp();
if (ocspResponse.getStatus() != OCSPResp.SUCCESSFUL)
{
throw new CertificateVerificationException("OCSP check not successful, status: "
+ ocspResponse.getStatus());
}
LOG.info("OCSP check successful");
BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject();
X509Certificate ocspResponderCertificate = ocspHelper.getOcspResponderCertificate();
if (ocspResponderCertificate.getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId()) != null)
{
// https://tools.ietf.org/html/rfc6960#section-4.2.2.2.1
// A CA may specify that an OCSP client can trust a responder for the
// lifetime of the responder's certificate. The CA does so by
// including the extension id-pkix-ocsp-nocheck.
LOG.info("Revocation check of OCSP responder certificate skipped (id-pkix-ocsp-nocheck is set)");
return;
}
LOG.info("Revocation check of OCSP responder certificate");
Set<X509Certificate> additionalCerts2 = new HashSet<X509Certificate>(additionalCerts);
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
for (X509CertificateHolder certHolder : basicResponse.getCerts())
{
try
{
X509Certificate cert = certificateConverter.getCertificate(certHolder);
if (!ocspResponderCertificate.equals(cert))
{
additionalCerts2.add(cert);
}
}
catch (CertificateException ex)
{
// unlikely to happen because the certificate existed as an object
LOG.error(ex, ex);
}
}
try
{
checkRevocations(ocspResponderCertificate, additionalCerts2, now);
}
catch (GeneralSecurityException ex)
{
throw new CertificateVerificationException(ex.getMessage(), ex);
}
LOG.info("Revocation check of OCSP responder certificate done");
}
}