The error

I recently came across the problem, that is described as spotbugs issue #1754: The latest spotbugs eclipse plugin cannot be installed in older eclipse installations anymore due to the following exception (complete stacktrace for search engines):

java.security.NoSuchAlgorithmException: An error occurred while processing the signatures for the file: /tmp/signatureFile1761899943782232543.jar
        at org.eclipse.osgi.internal.signedcontent.SignedBundleHook.getSignedContent(SignedBundleHook.java:231)
        at org.eclipse.equinox.internal.p2.artifact.repository.SignatureVerifier.verifyContent(SignatureVerifier.java:84)
        at org.eclipse.equinox.internal.p2.artifact.repository.SignatureVerifier.verify(SignatureVerifier.java:66)
        at org.eclipse.equinox.internal.p2.artifact.repository.SignatureVerifier.close(SignatureVerifier.java:115)
        at org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep.close(ProcessingStep.java:92)
        at org.eclipse.equinox.internal.p2.artifact.processors.checksum.MessageDigestProcessingStep.close(MessageDigestProcessingStep.java:58)
        at org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository.reportStatus(SimpleArtifactRepository.java:1250)
        at org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository.downloadArtifact(SimpleArtifactRepository.java:645)
        at org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository.getArtifact(SimpleArtifactRepository.java:776)
        at org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest.getArtifact(MirrorRequest.java:319)
        at org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest.transferSingle(MirrorRequest.java:289)
        at org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest.transfer(MirrorRequest.java:225)
        at org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest.perform(MirrorRequest.java:155)
        at org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository.getArtifact(SimpleArtifactRepository.java:759)
        at org.eclipse.equinox.internal.p2.artifact.repository.simple.DownloadJob.run(DownloadJob.java:64)
        at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
Caused by: java.security.NoSuchAlgorithmException: No algorithm found for 1.2.840.113549.1.1.11
        at org.eclipse.osgi.internal.signedcontent.PKCS7Processor.findEncryption(PKCS7Processor.java:95)
        at org.eclipse.osgi.internal.signedcontent.PKCS7Processor.processSignerInfos(PKCS7Processor.java:364)
        at org.eclipse.osgi.internal.signedcontent.PKCS7Processor.<init>(PKCS7Processor.java:172)
        at org.eclipse.osgi.internal.signedcontent.SignatureBlockProcessor.processSigner(SignatureBlockProcessor.java:109)
        at org.eclipse.osgi.internal.signedcontent.SignatureBlockProcessor.process(SignatureBlockProcessor.java:76)
        at org.eclipse.osgi.internal.signedcontent.SignedBundleFile.initializeSignedContent(SignedBundleFile.java:47)
        at org.eclipse.osgi.internal.signedcontent.SignedBundleHook.getSignedContent(SignedBundleHook.java:223)
        ... 15 more

So - something is not working here. I checked pmd-eclipse-plugin - and this can be installed fine in an older eclipse installation. The related eclipse bug #572034 talks about a changed signature algorithm of the use timestamp service (TSA). Also the stackoverflow question Eclipse p2 NoSuchAlgorithmException: No algorithm found for 1.2.840.113549.1.1.11 points to the same direction. And as I use the same service as spotbugs (see older post Code signing with let’s encrypt certificate for your github pages domain) I was afraid I’d face the same problem soon with pmd-eclipse-plugin. But no…

Digging with jarsigner

So, digging deeper - using jarsigner -verify -verbose:

This is the output of spotbugs:

$ jarsigner -verify -verbose com.github.spotbugs.plugin.eclipse_4.5.2.r202112132137-0c8b4d1.jar
...
- Signed by "CN=spotbugs.kengo-toda.jp"
    Digest algorithm: SHA-256
    Signature algorithm: SHA256withRSA, 2048-bit key
  Timestamped by "CN=DigiCert Timestamp 2021, O="DigiCert, Inc.", C=US" on Mo. Dez. 13 21:38:50 UTC 2021
    Timestamp digest algorithm: SHA-256
    Timestamp signature algorithm: SHA256withRSA, 2048-bit key

So, nothing strange. What about pmd-eclipse-plugin?

$ jarsigner -verify -verbose net.sourceforge.pmd.eclipse.plugin_4.31.0.v20211220-2043.jar
...
- Signed by "CN=pmd-code.org"
    Digest algorithm: SHA-256
    Signature algorithm: SHA256withRSA, 2048-bit key
  Timestamped by "CN=DigiCert Timestamp 2021, O="DigiCert, Inc.", C=US" on Mo. Dez. 20 20:43:35 UTC 2021
    Timestamp digest algorithm: SHA-256
    Timestamp signature algorithm: SHA256withRSA, 2048-bit key

I see no difference. The signature algorithm is always “SHA256withRSA” - of course, the timestamps differ. Also the key size (2048 bit) is the same. But spotbugs doesn’t install, while pmd-eclipse-plugin does.

How are the artifacts being signed?

Next: How does spotbugs sign the artifacts? Did they change something? It’s buried in the gradle script somewhere: github.com/spotbugs/spotbugs/blob/master/eclipsePlugin/build.gradle#L445-L466

// sign all .jar file under specified dir
ext.signJar = { File dir ->
  def keystorepass = project.hasProperty('keystorepass') ? keystorepass : '';
  if (keystorepass.isEmpty()) {
    print 'to sign eclipse plugins, set "keystorepass" project property'
    return
  }

  dir.traverse(type: FILES, nameFilter: ~/.*\.jar$/) {
    def relativePath = rootDir.toPath().relativize( it.toPath() )
    println "signing ${relativePath}"
    ant.signjar(
        destDir: it.parentFile,
        jar: it,
        alias: 'eclipse-plugin',
        keystore: "$rootDir/spotbugs.jks",
        storepass: keystorepass,
        tsaurl: 'http://timestamp.digicert.com',
        verbose: true
    )
  }
}

Still using Digicert’s TSA service. No special parameters.

Looking at the build log of the last release. Maybe “ant.signjar” does output some clue? The build for version 4.5.2 shows this:

> Task :eclipsePlugin:generateP2Metadata
signing eclipsePlugin/build/site/eclipse/features/com.github.spotbugs.plugin.eclipse_4.5.2.r202112132137-0c8b4d1.jar
signing eclipsePlugin/build/site/eclipse/plugins/com.github.spotbugs.plugin.eclipse_4.5.2.r202112132137-0c8b4d1.jar
Generating metadata for ..
Generation completed with success [0 seconds].

No clue. But I notice the step Set up JDK 17. Spotbugs obviously uses Java 17 for building. I know, pmd-eclipse-plugin uses Java 11. Is that maybe a difference? Spotbugs uses Java 17 since version 4.4.2, so the last version build with Java 11 is 4.4.1. And this works. Using jarsigner against this doesn’t show anything new - algorithm is “SHA256withRSA”.

A hunch - JDK 17

At least a hunch now. Is there a change between Java 11 and Java 17? The release notes don’t list something, that could be the reason. Only once jarsigner is mentioned: “jarsigner Tool Warns if Weak Algorithms Are Used in Signer’s Certificate Chain”. One problem is, I still see no difference in the jarsigner output - it’s always “SHA256withRSA”. But wait, which version was I using? Java 17 of course. Let’s see, what Java 11’s jarsigner will print out:

This is the output of spotbugs with Java 11’s jarsigner:

$ jarsigner -verify -verbose com.github.spotbugs.plugin.eclipse_4.5.2.r202112132137-0c8b4d1.jar
- Signed by "CN=spotbugs.kengo-toda.jp"
    Digest algorithm: SHA-256
    Signature algorithm: SHA256withSHA256withRSA, 2048-bit key
  Timestamped by "CN=DigiCert Timestamp 2021, O="DigiCert, Inc.", C=US" on Mo. Dez. 13 21:38:50 UTC 2021
    Timestamp digest algorithm: SHA-256
    Timestamp signature algorithm: SHA256withRSA, 2048-bit key

Oh - now we have SHA256withSHA256withRSA. Interesting.

For comparison: output of pmd-eclipse-plugin:

$ jarsigner -verify -verbose net.sourceforge.pmd.eclipse.plugin_4.31.0.v20211220-2043.jar
- Signed by "CN=pmd-code.org"
    Digest algorithm: SHA-256
    Signature algorithm: SHA256withRSA, 2048-bit key
  Timestamped by "CN=DigiCert Timestamp 2021, O="DigiCert, Inc.", C=US" on Mo. Dez. 20 20:43:35 UTC 2021
    Timestamp digest algorithm: SHA-256
    Timestamp signature algorithm: SHA256withRSA, 2048-bit key

The difference in PKCS #9 data structures

Aha. Now we can see a difference.

What is “1.2.840.113549.1.1.11” anyway? There is a nice website oidref.com, and for 1.2.840.113549.1.1.11 they show the node name sha256WithRSAEncryption. Apparently it is defined in RFC 4055.

Is there a different algorithm used in Java 17? jarsigner for java 11 docs list “SHA256withRSA” for RSA-Keys with less than or equal to 3072 bits. And jarsigner for java 17 docs specifies the same. So the two tools should use the same algorithm…

And why do the two tools print different algorithms for the same file? Maybe we can figure out the actual OID. We know that “1.2.840.113549.1.1.11” seems to be “SHA256withRSA” or “SHA256withSHA256withRSA” depending on the Java version. But which OID has been used for signing pmd-eclipse-plugin?

When the jarsigner signs the jar, it creates inside it the signature block with a .RSA extension. This is a DER encoded ASN1 structure, which can be looked at with openssl: openssl asn1parse -in ECLIPSE-.RSA -inform DER -i. However, there are also online tools, like ASN.1 JavaScript Decoder. This shows the structure of the signature.

Now let’s compare spotbugs with pmd-eclipse-plugin:

If you search here for the string “1.2.840.113549.1.1.11” there are 10 occurrences in spotbugs, but only 8 in pmd-eclipse-plugin. So, it’s not the problem, that “1.2.840.113549.1.1.11” wouldn’t be supported at all, it’s only not supported as it is used by Java 17.

Jarsigner differences

Java 17 seems to generate data structures from PKCS #9 additionally, like “1.2.840.113549.1.9.5 signingTime (PKCS #9)”. This is not the case with Java 11.

Digging deeper into the rabit hole: The source code of openjdk is on github - as well as the jarsigner. This class determines the default algorithm for creating the signature. This is delegated to AlgorithmId.getDefaultSigAlgForKey in Java 11:

https://github.com/openjdk/jdk/blob/da75f3c4ad5bdf25167a3ed80e51f567ab3dbd01/src/java.base/share/classes/sun/security/x509/AlgorithmId.java#L1032-L1055

/**
 * Returns the default signature algorithm for a private key. The digest
 * part might evolve with time. Remember to update the spec of
 * {@link jdk.security.jarsigner.JarSigner.Builder#getDefaultSignatureAlgorithm(PrivateKey)}
 * if updated.
 *
 * @param k cannot be null
 * @return the default alg, might be null if unsupported
 */
public static String getDefaultSigAlgForKey(PrivateKey k) {
    switch (k.getAlgorithm().toUpperCase(Locale.ENGLISH)) {
        case "EC":
            return ecStrength(KeyUtil.getKeySize(k))
                + "withECDSA";
        case "DSA":
            return ifcFfcStrength(KeyUtil.getKeySize(k))
                + "withDSA";
        case "RSA":
            return ifcFfcStrength(KeyUtil.getKeySize(k))
                + "withRSA";
        default:
            return null;
    }
}

This method has been moved into SignatureUtil for Java 17 and looks a bit different:

https://github.com/openjdk/jdk/blob/517967284cf607c0137e088a33ab5eb98d59542d/src/java.base/share/classes/sun/security/util/SignatureUtil.java#L480-L499

/**
 * Returns the default signature algorithm for a private key.
 *
 * @param k cannot be null
 * @return the default alg, might be null if unsupported
 */
public static String getDefaultSigAlgForKey(PrivateKey k) {
    String kAlg = k.getAlgorithm();
    return switch (kAlg.toUpperCase(Locale.ENGLISH)) {
        case "DSA", "RSA" -> ifcFfcStrength(KeyUtil.getKeySize(k))
                + "with" + kAlg;
        case "EC" -> ecStrength(KeyUtil.getKeySize(k))
                + "withECDSA";
        case "EDDSA" -> k instanceof EdECPrivateKey
                ? ((EdECPrivateKey) k).getParams().getName()
                : kAlg;
        case "RSASSA-PSS", "ED25519", "ED448" -> kAlg;
        default -> null;
    };
}

Now it returns additionally the key algorithm - which explains the change from “SHA256withRSA” to “SHA256withSHA256withRSA”.

But that can only be part of the picture, as we’ve seen above from the generated ASN.1 structure, there have been more changes.

Conclusions

  • Java 11’s jarsigner creates signed jar, that can be loaded by older eclipse version. Note, that the mentioned eclipse bug #572034 has been fixed for eclipse 4.20 (2021-06). If you can, just update eclipse.
  • To install the new spotbugs plugin into an older eclipse, you could manually remove the signature from the plugin-jar and feature-jar files. Then you’ll get a warning about unsigned code, but you can install it. You could also sign it yourself again with your own certificate.
  • Maybe you can still use Java 17’s jarsigner to create backwards compatible signatures by providing the signature algorithm explicitly (-sigalg SHA256withRSA). I’ve not tried this.
  • Otherwise you should keep using Java 11 in order to support older eclipse version (before 4.20 / 2021-06).