SME
SME

Reputation: 556

BouncyCastle signature creation java.lang.NoSuchFieldError: xmss_SHA256ph

I am trying to create a signature as shown below, but I am getting this error:

java.lang.NoSuchFieldError: xmss_SHA256ph

I am using bcprov-jdk15on and bcpkix-jdk15on version 1.64 and Java 8. I have tried various signature algorithms, the latest being SHA1WITHRSA. I have also tried SHA256WITHRSA and SHA256withECDSA.

Do you know why I am getting this error? Thanks.

CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder(getSignatureAlgorithm()).build(key);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new 
JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, cert));
gen.addCertificates(new JcaCertStore(chain));
CMSTypedDataInputStream msg = new CMSTypedDataInputStream(content);
CMSSignedData signedData = gen.generate(msg, false);
signatureBytes = signedData.getEncoded();

The stack trace

java.lang.NoSuchFieldError: xmss_SHA256ph
at org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder.<clinit>(Unknown Source) ~[bcpkix-jdk15on-1.64.jar:1.64.00.0]
at org.bouncycastle.operator.jcajce.JcaContentSignerBuilder.<init>(Unknown Source) ~[bcpkix-jdk15on-1.64.jar:1.64.00.0]
at com.trovare.document.pki.Signer.sign(Signer.java:162) ~[classes/:na]
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteSignature(COSWriter.java:744) ~[pdfbox-2.0.19.jar:2.0.19]
at org.apache.pdfbox.pdfwriter.COSWriter.visitFromDocument(COSWriter.java:1150) ~[pdfbox-2.0.19.jar:2.0.19]
at org.apache.pdfbox.cos.COSDocument.accept(COSDocument.java:452) ~[pdfbox-2.0.19.jar:2.0.19]
at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1386) ~[pdfbox-2.0.19.jar:2.0.19]
at org.apache.pdfbox.pdmodel.PDDocument.saveIncremental(PDDocument.java:1392) ~[pdfbox-2.0.19.jar:2.0.19]
at com.trovare.document.pdf.PdfDcoumentSigner.sign(PdfDcoumentSigner.java:167) ~[classes/:na]
at com.trovare.document.DocumentEncryptorApplication.run(DocumentEncryptorApplication.java:62) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:768) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at com.trovare.document.DocumentEncryptorApplication.main(DocumentEncryptorApplication.java:48) [classes/:na]

I created a new key store and key for each algorithm I tested, using the java keytool. Like this, for example:

keytool -genkey -alias docsigner -keyalg RSA -keysize 2048 -sigalg SHA256withRSA  -validity 3650 -keystore keystore.jks

Upvotes: 5

Views: 7571

Answers (3)

Pawel Veselov
Pawel Veselov

Reputation: 4225

The underlying reason for errors like this is having more than one instance of Bouncy Castle (BC) implementation in your runtime path. This can manifest in other ways, but this (NoSuchFieldError in JcaContentSignerBuilder initialization) is common, as the latter initializes a bunch of fields referencing algorithm identifiers by accessing corresponding fields. If the BC component that carries the identifiers is older, this particular error will show up (BC authors regularly add new algorithms support). It's also one of the easiest to point to version incompatibility, by the way, others can be a lot more subtle.

Now, with practically any other dependency, such is not easy to achieve when using proper build tools (i.e., Maven or Gradle), BC is quite prone to that by means of:

  1. Project, or one of its dependency declares dependency on one or few (but not all) components of BC version A, and another dependency or dependencies declare a dependency on other/all components of BC of version B. This is what happened with OP in this case. The result is a mixture of Bouncy Castle components with different versions, and are going to be incompatible in some way. The solution to this case is to explicitly list version of all known BC components that are used (note that you may not even have direct dependencies on BC, and still need to do that).

  2. BC has different flavors of their libraries, at least those of jdk15on, jdk18on and jdk15to18. Multiple flavors should not be used together, intermixed, and/or be of different versions. However, because the flavors come with different artifact IDs, simply listing all versions/components you want to use is not enough (Your build system does not consider jdk15on and jdk18on to be "the same thing" and eligible for collapsing into one). If your code declares a need for BC's jdk15on version A, but a dependency of your code needs jdk18on, whether the same or a different version, your build system will use both. To solve this, you'd have to go through all of your direct dependencies, and instruct your build system to not pull the BC dependencies explicitly. As long as the flavor of BC is suitable for the version of the JDK you use at runtime - things should work out.

So, anytime you see weird exceptions involving BC code, especially when used from well-established libraries, the first to do is to get a full dependency tree of your entire project, and ensure that everywhere in the tree you have:

  • Only one flavor of BC used
  • Only one version of BC used

And then keep modifying your build system files until that's achieved.

Upvotes: 0

Olaf
Olaf

Reputation: 792

I encountered this but wasn't satisfied by the existing answers which said "just use an old version"!

In my case, I had been managing the following dependencies:

  <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.68</version>
  </dependency>
  <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.68</version>
  </dependency>

After a bit of code inspection and looking at the dependency hierarchy, I saw another bouncycastle dependency was being pulled in that was out of sync, namely:

[INFO] +- org.springframework.security.extensions:spring-security-saml2-core:jar:1.0.10.RELEASE:compile
[INFO] |  +- com.narupley:not-going-to-be-commons-ssl:jar:0.3.20:compile
[INFO] |  |  +- org.bouncycastle:bcprov-ext-jdk15on:jar:1.60:compile

Managing the additional dependency to be consistent with the other bouncycastle ones resolved this for me, i.e. I added the following block to dependencyManagement in my POM:

  <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-ext-jdk15on</artifactId>
    <version>1.68</version>
  </dependency>

Upvotes: 7

SME
SME

Reputation: 556

So, for anyone else who has this issue, mine was fixed by change the version of bouncycastle to 1.60.

I should point out that I tried versions: 1.63, 1.62 and 1.61 and none of the worked. This is what my pom looks like now (or parts of it anyway)

<properties>
    <java.version>1.8</java.version>
    <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
    <bouncycastle.version>1.60</bouncycastle.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>${bouncycastle.version}</version>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>${bouncycastle.version}</version>
    </dependency>
    ...
<dependencies>

Upvotes: 3

Related Questions