Search code examples
javabouncycastlepdfbox

exception optaining signature: Could not verify signature


I have the following code, which is taken from ShowSignature example from PDFBox 2. I'm running five tests in which it is called, 4 of them pass without problem (no signature, single signed, double signed, expired signature), but the fifth one is wit Eliptic Curve and it fails.

The fun part is that it passes when I start the JunitTest only on the test-class, but fails as soon as I'm starting it on package or project level.

I would assume something befor the test class is interfering, but can't find a hint what it could be. I checked bouncycastle (1.54 is always used), the java jdk is at any point correctly used (jdk1.8.0_181).

I'm checking the for an exception to be thrown (ERROR_VERIFYING_PDF_SIGNATURE), it is thrown in the classtest, but a different one when starting on higher level.

Error occurs in if (signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData)) and has this stack trace:

java.lang.AssertionError: 
Expected: (an instance of de.bdr.rt.core.common.api.BusinessLogicException and exception with message a string containing "Die Signatur des PDF-Dokuments konnte nicht verifiziert werden.")
 but: an instance of de.bdr.rt.core.common.api.BusinessLogicException <org.bouncycastle.operator.RuntimeOperatorException: exception obtaining signature: Could not verify signature> is a org.bouncycastle.operator.RuntimeOperatorException
Stacktrace was: org.bouncycastle.operator.RuntimeOperatorException: exception obtaining signature: Could not verify signature
at org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder$SigVerifier.verify(Unknown Source)
at org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder$RawSigVerifier.verify(Unknown Source)
at org.bouncycastle.cms.SignerInformation.doVerify(Unknown Source)
at org.bouncycastle.cms.SignerInformation.verify(Unknown Source)
at de.bdr.gematik.tsp.sc.antragsverwaltung.impl.itsp.ds.PDFDigitalSignatureCheckTest.verifyPKCS7(PDFDigitalSignatureCheckTest.java:280)
at de.bdr.gematik.tsp.sc.antragsverwaltung.impl.itsp.ds.PDFDigitalSignatureCheckTest.testPdfSignature(PDFDigitalSignatureCheckTest.java:170)
at de.bdr.gematik.tsp.sc.antragsverwaltung.impl.itsp.ds.PDFDigitalSignatureCheckTest.testPDFECKeySignaturFails(PDFDigitalSignatureCheckTest.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.security.SignatureException: Could not verify signature
at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)
at java.security.Signature$Delegate.engineVerify(Signature.java:1222)
at java.security.Signature.verify(Signature.java:655)
at org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder$SignatureOutputStream.verify(Unknown Source)
... 32 more
Caused by: java.security.InvalidAlgorithmParameterException
at sun.security.ec.ECDSASignature.verifySignedDigest(Native Method)
at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:321)
... 35 more

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at org.junit.rules.ExpectedException.handleException(ExpectedException.java:252)
at org.junit.rules.ExpectedException.access$000(ExpectedException.java:106)
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:241)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

Code:

   private void verifyPKCS7(byte[] byteArray, COSString contents, PDSignature sig)
        throws CMSException, CertificateException, StoreException, OperatorCreationException,
        NoSuchAlgorithmException, NoSuchProviderException {
    CMSProcessable signedContent = new CMSProcessableByteArray(byteArray);
    CMSSignedData signedData = new CMSSignedData(signedContent, contents.getBytes());
    @SuppressWarnings("unchecked")
    Store<X509CertificateHolder> certificatesStore = signedData.getCertificates();
    Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
    SignerInformation signerInformation = signers.iterator().next();
    @SuppressWarnings("unchecked")
    Collection<X509CertificateHolder> matches = certificatesStore
            .getMatches((Selector<X509CertificateHolder>) signerInformation.getSID());
    X509CertificateHolder certificateHolder = matches.iterator().next();
    X509Certificate certFromSignedData = new JcaX509CertificateConverter().getCertificate(certificateHolder);
    System.out.println("certFromSignedData: " + certFromSignedData);
    try {
        certFromSignedData.checkValidity(sig.getSignDate().getTime());
        System.out.println("Certificate valid at signing time");
    } catch (CertificateExpiredException ex) {
        System.err.println("Certificate expired at signing time");
    } catch (CertificateNotYetValidException ex) {
        System.err.println("Certificate not yet valid at signing time");
    }

    if (isSelfSigned(certFromSignedData)) {
        System.err.println("Certificate is self-signed, LOL!");
    } else {
        System.out.println("Certificate is not self-signed");
        // todo rest of chain
    }

    if (signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData))) {
        System.out.println("Signature verified");
    } else {
        System.out.println("Signature verification failed");
        throw new BusinessLogicException(Messages.ERROR_VERIFYING_PDF_SIGNATURE);
    }
}

Solution

  • This is a shortcoming of the PDFBox example. Please change

    signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certFromSignedData));
    

    to

    signerInformation.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(certFromSignedData));
    

    you may have to register BC as provider:

    Security.addProvider(new BouncyCastleProvider());