Azure / azure-sdk-for-java

This repository is for active development of the Azure SDK for Java. For consumers of the SDK we recommend visiting our public developer docs at https://docs.microsoft.com/java/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-java.
MIT License
2.32k stars 1.97k forks source link

[BUG] jarsigner sign failed when using certs with non-exportable key #39590

Closed grant-arqit closed 3 weeks ago

grant-arqit commented 5 months ago

Describe the bug I have an issue using signing jars with the com.azure.security.keyvault.jca.KeyVaultJcaProvider provider using a certificate with a non-exportable key

Exception or Stack Trace Using the jarsigner cli tool I get the following error

jarsigner: unable to sign jar: java.security.InvalidKeyException: No installed provider supports this key: com.azure.security.keyvault.jca.implementation.KeyVaultPrivateKey

From the coded solution

Error signing jar: Error in signer materials
java.lang.RuntimeException: jdk.security.jarsigner.JarSignerException: Error in signer materials
    at uk.arqit.support.service.SignerService.signJar(SignerService.kt:28)
    at uk.arqit.support.SignerCommand.run(SignerCommand.kt:25)
    at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
    at picocli.CommandLine.access$1500(CommandLine.java:148)
    at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
    at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
    at picocli.CommandLine.execute(CommandLine.java:2170)
    at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:137)
    at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:114)
    at uk.arqit.support.SignerCommand$Companion.main(SignerCommand.kt:30)
    at uk.arqit.support.SignerCommand.main(SignerCommand.kt)
Caused by: jdk.security.jarsigner.JarSignerException: Error in signer materials
    at jdk.jartool/jdk.security.jarsigner.JarSigner.sign(JarSigner.java:564)
    at uk.arqit.support.service.SignerService.signJar(SignerService.kt:25)
    ... 13 more
Caused by: java.security.InvalidKeyException: No installed provider supports this key: com.azure.security.keyvault.jca.implementation.KeyVaultPrivateKey
    at java.base/java.security.Signature$Delegate.chooseProvider(Signature.java:1303)
    at java.base/java.security.Signature$Delegate.engineInitSign(Signature.java:1388)
    at java.base/java.security.Signature.initSign(Signature.java:684)
    at java.base/java.security.Signature$1.initSign(Signature.java:147)
    at java.base/sun.security.util.SignatureUtil.initSignWithParam(SignatureUtil.java:189)
    at java.base/sun.security.util.SignatureUtil.autoInitInternal(SignatureUtil.java:372)
    at java.base/sun.security.util.SignatureUtil.fromKey(SignatureUtil.java:364)
    at java.base/sun.security.pkcs.PKCS7.generateNewSignedData(PKCS7.java:750)
    at jdk.jartool/jdk.security.jarsigner.JarSigner.sign0(JarSigner.java:848)
    at jdk.jartool/jdk.security.jarsigner.JarSigner.sign(JarSigner.java:554)
    ... 14 more

To Reproduce Following the steps documented in https://github.com/backwind1233/AzureDocs/blob/main/AzureJavaSDK/JCA/integrate_keyvault_JCA_provider_with_jarsigner.md

jarsigner -verbose -J-Djava.security.debug=jar \
-keystore NONE \
-storetype AzureKeyVault \
-signedjar appsigned.jar app.jar "redacted" \
-storepass ' ' \
-sigalg SHA256withRSA \
-tsa http://timestamp.entrust.net/rfc3161ts2 \
-providerName AzureKeyVault \
-providerClass com.azure.security.keyvault.jca.KeyVaultJcaProvider \
-J--module-path="/home/grant/projects/cosign/azure-security-keyvault-jca-2.8.1.jar" \
-J--add-modules="com.azure.security.keyvault.jca" \
-J-Dazure.keyvault.uri=redacted \
-J-Dazure.keyvault.tenant-id=redacted \
-J-Dazure.keyvault.client-id=redacted \
-J-Dazure.keyvault.client-secret=redacted

I'm using the SHA256withRSA signature algorithm

See the same issue in JDK 17 and 21

I see the same error with the coded solution (snippet below).

Debugging this code, it would appear that the KeyVaultPrivateKey is not a supported key type. The method supportsKeyClass inside the java.security.Provider module of the JDK expects keys to implement one of the following interfaces

interface java.security.interfaces.RSAPrivateKey interface java.security.interfaces.RSAPublicKey

Code Snippet Test code

@Singleton
class SignerService(private val keyVaultClient: KeyVaultClient) : ISignerService {
    override fun signJar(alias: String, inputFilePath: String, outputFilePath: String) {
        val privateKey = keyVaultClient.getKey(alias, null)
        val certificate = keyVaultClient.getCertificate(alias)

        val jarSigner = JarSigner.Builder(KeyStore.PrivateKeyEntry(privateKey as PrivateKey, arrayOf(certificate)))
            .digestAlgorithm("SHA-256")
            .signatureAlgorithm("SHA256withRSA")
            .build()

        try {
            val inZip = ZipFile(inputFilePath)
            val outputStream = FileOutputStream(outputFilePath)
            jarSigner.sign(inZip, outputStream)
        } catch(e: Throwable) {
            println("Error signing jar: ${e.message}")
            throw RuntimeException(e)
        }
    }
}

Expected behavior I would expect KeyValutPrivateKey to be recognised as a valid key class and for signing inside the key vault to be actioned.

Screenshots If applicable, add screenshots to help explain your problem.

Setup (please complete the following information):

Additional context N/A

Information Checklist Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report

github-actions[bot] commented 5 months ago

@chenrujun @moarychan @netyyyy @saragluna

github-actions[bot] commented 5 months ago

Thank you for your feedback. Tagging and routing to the team member best able to assist.

grant-arqit commented 5 months ago

In addition, the keytype I am testing with in the vault is RSA-HSM. Have re-run CLI and code solution tests again with RSASSA-PSS sign algorithm as per README.md, but am seeing the same outcome.

johpf commented 2 months ago

Hi, We ran into the same problem today. Will this be fixed in the near future? Thanks!

rujche commented 2 months ago

Sorry for the late response.

I use the following code and it sign successfully:

KeyVaultJcaProvider provider = new KeyVaultJcaProvider();
Security.addProvider(provider);
String alias = "signer240727";
PrivateKey privateKey = (PrivateKey) keyVaultClient.getKey(alias, null);
Certificate certificate = keyVaultClient.getCertificate(alias);

JarSigner jarSigner = new JarSigner.Builder(new KeyStore.PrivateKeyEntry(privateKey, new Certificate[]{certificate}))
    .digestAlgorithm("SHA-256")
    .signatureAlgorithm("SHA256withRSA")
    .build();

ZipFile inZip = new ZipFile("C:\\Users\\rujche\\Work\\problem-investigation\\jca\\unsigned.jar");
OutputStream outputStream = new FileOutputStream("C:\\Users\\rujche\\Work\\problem-investigation\\jca\\signedJar.jar");
jarSigner.sign(inZip, outputStream);

With out the following 2 lines, I faced this error: No installed provider supports this key: com.azure.security.keyvault.jca.implementation.KeyVaultPrivateKey.

KeyVaultJcaProvider provider = new KeyVaultJcaProvider();
Security.addProvider(provider);
johpf commented 2 months ago

Hi @rujche,

I am still running into the same Problem. We have an Azure Key Vault with an older certificate + key pair which works completely fine. A few weeks ago we got a new, keyless certificate. This one still works with the AzuerSignTool but not anymore with the jarsigner. I am using this command: jarsigner -keystore NONE -storetype AzureKeyVault \ -signedjar C:\Users\generic\Desktop\testjar_after.jar C:\Users\generic\Desktop\testjar.jar "REDACTED" \ -verbose -J-Djava.security.debug=jar \ -storepass "" -sigalg SHA256withRSA \ -providerName AzureKeyVault \ -providerClass com.azure.security.keyvault.jca.KeyVaultJcaProvider \ -tsa http://timestamp.globalsign.com/tsa/r6advanced1 \ -J--module-path="C:\utl\java\azure-security-keyvault-jca-2.8.1.jar" \ -J--add-modules="com.azure.security.keyvault.jca" \ -J-Dazure.keyvault.uri=REDACTED \ -J-Dazure.keyvault.tenant-id=REDACTED \ -J-Dazure.keyvault.client-id=REDACTED \ -J-Dazure.keyvault.client-secret=REDACTED

Which yields: jarsigner: unable to sign jar: java.security.InvalidKeyException: No installed provider supports this key: com.azure.security.keyvault.jca.implementation.KeyVaultPrivateKey

The Key Type is RSA-HSM, The Key Size is 4096.

Any advice would be greatly appreciated.

rujche commented 2 months ago

As show above, this is a bug which will be fixed by this commit in https://github.com/Azure/azure-sdk-for-java/pull/41303

johpf commented 2 months ago

Hi, @rujche Thanks so much!