Signing with GCP KMS requires the algorithm to be appended to the alias
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?
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.
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.