jsign icon indicating copy to clipboard operation
jsign copied to clipboard

Signing with GCP KMS requires the algorithm to be appended to the alias

Open efetoboreakpoguma opened this issue 1 year ago • 2 comments

I am currently attempting to perform code signing using a private key stored in Google Cloud KMS and a code signing certificate. I discovered it fails when I don't include the algorithm in the alias (key name).

For example, this fails:

jarsigner -J-cp -Jjsign.jar `
	-J--add-modules -Jjava.sql -providerClass net.jsign.jca.JsignJcaProvider `
	-providerArg projects/project/locations/global/keyRings/keyRing -keystore NONE `
	-storetype GOOGLECLOUD -storepass <REDACTED> -certchain signing_cert.pem `
	-tsa http://timestamp.digicert.com -strict -debug D:\fileToSign.jar key/cryptoKeyVersions/1

and this passes:

jarsigner -J-cp -Jjsign.jar `
	-J--add-modules -Jjava.sql -providerClass net.jsign.jca.JsignJcaProvider `
	-providerArg projects/project/locations/global/keyRings/keyRing -keystore NONE `
	-storetype GOOGLECLOUD -storepass <REDACTED> -certchain signing_cert.pem `
	-tsa http://timestamp.digicert.com -strict -debug D:\fileToSign.jar key/cryptoKeyVersions/2:RSA

because it contains :RSA.

I discovered that it passes because there is a check for the colon here in the alias name so we never have to check the signing certificate. However, when I don't include the algorithm in the alias, it fails with this exception:

jarsigner error: java.lang.RuntimeException: Failed to load the certificate from 
 java.lang.RuntimeException: Failed to load the certificate from 
     at net.jsign.KeyStoreType.lambda$getCertificateStore$0(KeyStoreType.java:565)
     at net.jsign.jca.GoogleCloudSigningService.getCertificateChain(GoogleCloudSigningService.java:102)
     at net.jsign.jca.GoogleCloudSigningService.getPrivateKey(GoogleCloudSigningService.java:126)
     at net.jsign.jca.SigningServiceKeyStore.engineGetKey(SigningServiceKeyStore.java:36)
     at java.base/java.security.KeyStore.getKey(KeyStore.java:1057)
     at net.jsign.jca.JsignJcaProvider$JsignJcaKeyStore.engineGetKey(JsignJcaProvider.java:122)
     at java.base/java.security.KeyStore.getKey(KeyStore.java:1057)
     at jdk.jartool/sun.security.tools.jarsigner.Main.getAliasInfo(Main.java:2276)
     at jdk.jartool/sun.security.tools.jarsigner.Main.run(Main.java:282)
     at jdk.jartool/sun.security.tools.jarsigner.Main.main(Main.java:129)
 Caused by: java.io.FileNotFoundException: 
     at java.base/java.io.FileInputStream.open0(Native Method)
     at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
     at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
     at net.jsign.CertificateUtils.loadCertificateChain(CertificateUtils.java:44)
     at net.jsign.KeyStoreType.lambda$getCertificateStore$0(KeyStoreType.java:563)
     ... 9 more

As you can see, the certificate file cannot be found. The exception gets thrown from here because params.certfile() is empty.

Is this behaviour expected or have I stumbled across a bug?

efetoboreakpoguma avatar Jan 21 '25 22:01 efetoboreakpoguma

Thank your for the report, that's indeed a bug. GoogleCloudSigningService assumes that a certificate file was provided, because this is enforced by KeyStoreType.GOOGLECLOUD.validate(). But the Jsign JCA provider used by jarsigner bypasses this validation, and jarsigner doesn't pass the certificate file to the signing service, so this leads to an exception when loading the certificate. I'll fix this.

That said, appending the algorithm is the best approach since it saves a call to the GCP API. This isn't well documented.

ebourg avatar Jan 22 '25 00:01 ebourg

I agree. Appending the algorithm is a better approach. I have created a PR for a possible solution. Please let me know what you think.

efetoboreakpoguma avatar Jan 29 '25 14:01 efetoboreakpoguma