Our CA, GlobalSign has issued a code signing certificate meant for an HSM. I created a Premium licensed Key Vault in Azure since the basic doesn’t support HSM. I then generated a certificate in the Key Vault using the following Issuance Policy:
- Type of Certificate Authority (CA): Certificate issued by a
non-integrated CA - Subject: C=US, L=Doeville, O=JohnDoe, CN=JohnDoe,
[email protected] - DNS Names: 0 DNS names
- Validity Period (in months): 24
- Content Type: PKCS#12
- Enabled: Yes
Advanced Policy Configuration
- Extended Key Usages (EKUs): 1.3.6.1.5.5.7.3.3
- X.509 Key Usage Flags: Digital Signature, Key Encipherment
- Reuse Key on Renewal?: No
- Exportable Private Key?: No
- Key Type: RSA-HSM
- Key Size: 4096
- Enable Certificate Transparency?: Yes
- Certificate Type: {Empty}
Once it was instanced in the key vault, I clicked on Certificate Operation and downloaded the CSR.
I followed the download link provided by Global Sign and copied and pasted the CSR. After the certificates had been generated, I got a page where I could download the Digital certificate and 2 intermediate certificates. The file names were: OS123456789.cer, intermediate1.cer and intermediate2.cer
I then went back to the Azure Key Vault and click on “Merge signed request” I then picked the OS123456789.cer file, and it was successful.
I then started getting to work on using jarsigner to sign my jars.
I downloaded azure-security-keyvault-jca-2.8.1.jar and placed it in the %JAVA_HOME&/jre/lib/ext
The jarsigner command I use is this:
jarsigner -keystore NONE -storetype AzureKeyVault -verbose -storepass "" -tsa http://timestamp.globalsign.com/tsa/r6advanced1 -providerName AzureKeyVault -providerClass com.azure.security.keyvault.jca.KeyVaultJcaProvider -J"-Dazure.keyvault.uri=$KEYVAULT_URL" -J"-Dazure.keyvault.tenant-id=$TENANT" -J"-Dazure.keyvault.client-id=$CLIENT_ID" -J"-Dazure.keyvault.client-secret=$CLIENT_SECRET" -signedjar "signed.jar" "unsigned.jar" $CERT_ALIAS
The signing output looks like this:
updating: META-INF/MANIFEST.MF
adding: META-INF/GLOBALSI.SF
requesting a signature timestamp
TSA location: http://timestamp.globalsign.com/tsa/r6advanced1
adding: META-INF/GLOBALSI.RSA
A BUNCH OF FILES
signing: clientlogging.properties
adding: JNLP-INF/
signing: JNLP-INF/APPLICATION_TEMPLATE.JNLP
>>> Signer
X.509, [email protected], CN=JohnDoe, O=JohnDoe, L=DoeVille, C=US
[trusted certificate]
>>> TSA
X.509, CN=Globalsign TSA for CodeSign1 - R6 - 202311, O=GlobalSign nv-sa, C=BE
[certificate is valid from 07/11/23 18:13 to 09/12/34 18:13]
X.509, CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
[certificate is valid from 20/06/18 02:00 to 10/12/34 01:00]
X.509, CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
[trusted certificate]
jar signed.
The timestamp will expire on 2034-12-09.
It looks good so far, I think.
However, when I run:
jarsigner -verify -verbose -certs signed.jar
I get the following output:
s 291951 Tue Sep 24 12:09:10 CEST 2024 META-INF/MANIFEST.MF
>>> Signer
X.509, [email protected], CN=JohnDoe, O=JohnDoe, L=DoeVille, C=US
[certificate is valid from 20/09/24 16:00 to 03/09/27 14:19]
[Invalid certificate chain: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
>>> TSA
X.509, CN=Globalsign TSA for CodeSign1 - R6 - 202311, O=GlobalSign nv-sa, C=BE
[certificate is valid from 07/11/23 18:13 to 09/12/34 18:13]
X.509, CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
[certificate is valid from 20/06/18 02:00 to 10/12/34 01:00]
X.509, CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
[trusted certificate]
292002 Tue Sep 24 12:09:12 CEST 2024 META-INF/GLOBALSI.SF
8401 Tue Sep 24 12:09:12 CEST 2024 META-INF/GLOBALSI.RSA
0 Tue Sep 10 16:47:38 CEST 2024 META-INF/
A BUNCH OF FILES
sm 1111 Tue Sep 10 16:47:36 CEST 2024 JNLP-INF/APPLICATION_TEMPLATE.JNLP
[entry was signed on 24/09/24 12:09]
>>> Signer
X.509, [email protected], CN=JohnDoe, O=JohnDoe, L=DoeVille, C=US
[certificate is valid from 20/09/24 16:00 to 03/09/27 14:19]
[Invalid certificate chain: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
>>> TSA
X.509, CN=Globalsign TSA for CodeSign1 - R6 - 202311, O=GlobalSign nv-sa, C=BE
[certificate is valid from 07/11/23 18:13 to 09/12/34 18:13]
X.509, CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
[certificate is valid from 20/06/18 02:00 to 10/12/34 01:00]
X.509, CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
[trusted certificate]
s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
i = at least one certificate was found in identity scope
Signed by "[email protected], CN=JohnDoe, O=JohnDoe, L=DoeVille, C=US"
Digest algorithm: SHA-256
Signature algorithm: SHA256withRSA, 4096-bit key
Timestamped by "CN=Globalsign TSA for CodeSign1 - R6 - 202311, O=GlobalSign nv-sa, C=BE" on Tue Sep 24 10:09:12 UTC 2024
Timestamp digest algorithm: SHA-256
Timestamp signature algorithm: SHA256withSHA256withRSA, 3072-bit key
jar verified.
Warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
The signer certificate will expire on 2027-09-03.
The timestamp will expire on 2034-12-09.
The verification fails with the following warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
As far as I understand, this occurs because it is unable to determine a chain of trust to the CA.
I then used keytool to import the intermediate certificates into my local keystore located under my home directory. I then signed the jar files again and this time jarsigner was able to verify the jar file without any warnings.
However, when I then deploy the java application and the users download the jnlp file and attempt to install the java application, they get a warning popup saying that the signed application is not trusted.
We are using java 8. Tried both the latest Amazon Corretto and Oracles
My questions are:
- Do you have any ideas on how to solve this?
- Is it possible to include the intermediate certs when merging the signed request, and will that solve the issue?
Thanks for any assistance you can be provided.
I was able to figure it out.
You need to provide the certificate trust chain as a pem file to jarsigner via the -certchain option.
This is the correct command:
jarsigner -certchain trustchain.pem -keystore NONE -storetype AzureKeyVault -verbose -storepass "" -tsa http://timestamp.globalsign.com/tsa/r6advanced1 -providerName AzureKeyVault -providerClass com.azure.security.keyvault.jca.KeyVaultJcaProvider -J"-Dazure.keyvault.uri=$KEYVAULT_URL" -J"-Dazure.keyvault.tenant-id=$TENANT" -J"-Dazure.keyvault.client-id=$CLIENT_ID" -J"-Dazure.keyvault.client-secret=$CLIENT_SECRET" -signedjar "signed.jar" "unsigned.jar" $CERT_ALIAS
To create the pem file you need to concatenate the code signing certificate with the intermediate certificates. The order is important:
cat codesigner.cer, intermediate1.cer, intermediate.cer > bundle.pem
You can also use a text editor to combine the certificates.
You will know it works when you run jarsigner with the -verbose option and see the complete trust chain:
>>> Signer
X.509, [email protected], CN=JohnDoe, O=JohnDoe, L=DoeVille, C=US
[certificate is valid from 20/09/24 16:00 to 03/09/27 14:19]
X.509, CN=GlobalSign Code Signing Root R45, O=GlobalSign nv-sa, C=BE
[trusted certificate]
X.509, CN=GlobalSign GCC R45 CodeSigning CA 2020, O=GlobalSign nv-sa, C=BE
[trusted certificate]