Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Unable to retrieve Private key for an Certificate with exportable keys #32106

Open
3 tasks
vtapadia opened this issue Nov 11, 2022 · 16 comments
Open
3 tasks
Assignees
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. feature-request This issue requires a new behavior in the product in order be resolved. KeyVault needs-team-attention This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that

Comments

@vtapadia
Copy link

Describe the bug
Using the KeyVaultClient, we are unable to retrieve the private key from the Key Vault.

Exception or Stack Trace
2022-11-11 16:05:55,344 INFO | main | example.certificate.UserMgmtTest | Key Properties exportable ?: null
2022-11-11 16:05:55,344 INFO | main | example.certificate.UserMgmtTest | Private Key ?: false
2022-11-11 16:05:55,344 ERROR | main | com.azure.security.keyvault.keys.models.JsonWebKey | java.security.NoSuchAlgorithmException: no such algorithm: RSA for provider AzureKeyVault
java.lang.IllegalStateException: java.security.NoSuchAlgorithmException: no such algorithm: RSA for provider AzureKeyVault
at com.azure.security.keyvault.keys.models.JsonWebKey.getRsaPublicKey(JsonWebKey.java:583)
at com.azure.security.keyvault.keys.models.JsonWebKey.toRsa(JsonWebKey.java:741)

To Reproduce
Create a self signed certificate in Azure Key Vault, with Advanced option to enable export of keys.
And try to download the private keys using the KeyVaultClient

Code Snippet

        KeyClient keyClient = new KeyClientBuilder()
                .credential(credential)
                .vaultUrl(kvURL)
                .buildClient();

        KeyVaultKey keyVaultKey = keyClient.getKey(keyId);
        log.info("Key Properties exportable ?: {}" , keyVaultKey.getProperties().isExportable());

        JsonWebKey webKey = keyVaultKey.getKey();
        log.info("Private Key ?: {}" , webKey.hasPrivateKey());

Expected behavior
Should be able to extract the private key in this case.

Screenshots
N/A
Setup (please complete the following information):

  • OS: [e.g. iOS]
  • IDE: [e.g. IntelliJ]
  • Library/Libraries: com.azure:azure-security-keyvault-keys:4.5.1
  • Java version:17
  • App Server/Environment: [e.g. Tomcat, WildFly, Azure Function, Apache Spark, Databricks, IDE plugin or anything special]
  • Frameworks: [e.g. Spring Boot, Micronaut, Quarkus, etc]

If you suspect a dependency version mismatch (e.g. you see NoClassDefFoundError, NoSuchMethodError or similar), please check out Troubleshoot dependency version conflict article first. If it doesn't provide solution for the problem, please provide:

  • verbose dependency tree (mvn dependency:tree -Dverbose)
  • exception message, full stack trace, and any available logs

Additional context
While creating the certificate, the Private key was marked as exportable
image

Access permissions are set as
image

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

  • Bug Description Added
  • Repro Steps Added
  • Setup information Added
@ghost ghost added needs-triage This is a new issue that needs to be triaged to the appropriate team. customer-reported Issues that are reported by GitHub users external to the Azure organization. question The issue doesn't require a change to the product in order to be resolved. Most issues start as that labels Nov 11, 2022
@joshfree joshfree added KeyVault Client This issue points to a problem in the data-plane of the library. labels Nov 15, 2022
@ghost ghost removed the needs-triage This is a new issue that needs to be triaged to the appropriate team. label Nov 15, 2022
@joshfree
Copy link
Member

@vcolin7 could you please follow up with @vtapadia on this issue? thanks

@vcolin7
Copy link
Member

vcolin7 commented Nov 16, 2022

Last I checked you cannot export keys in Key Vault instances, only in Managed HSMs. Is this right @jlichwa? Which one are you working with @vtapadia?

@jlichwa
Copy link

jlichwa commented Nov 16, 2022

@vtapadia you should use new SDK versus deprecated KeyClient SDK.

Either way, it is certificate, certificate can be retrieved as PFX/PEM with private key using GetSecret API/SDK. I don't see sample in java but in C#:
https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/keyvault/samples/getcert/Program.cs#L71

@vcolin7
Copy link
Member

vcolin7 commented Nov 17, 2022

@jlichwa He's using the new KeyClient (the deprecated one is called KeyVaultClient). @vtapadia one thing to note is that the code sample shared only works for getting a certificate's private key.

@jlichwa, do you know if a KeyVaultKey's private key (not part of a certificate) can even be exported from a Key Vault? I thought we only supported this for Managed HSMs.

@vcolin7
Copy link
Member

vcolin7 commented Nov 17, 2022

@vtapadia While we wait for an answer from Jack, if I recall correctly, you will need to set a release policy for a key before being able to gain access to its private key, even if you set the exportable property to true. You can find more information on how the release operation works here and about the release policy itself here.

You can find how we do this for one of our tests here.

Adding @heaths to the conversation just in case, please feel free to correct me if I'm wrong about this.

@jlichwa
Copy link

jlichwa commented Nov 17, 2022

@vcolin7 it is different key. You think about cryptographic key. It is private key of certificate. Certificates does not have release policy (based on screenshot it is certificate).

@heaths
Copy link
Member

heaths commented Nov 17, 2022

The private key material is never part of the certificate but is stored as a secret. Java might consider implementing a downloadCertificate methods on CertificateClient like I did in .NET; though, customers can do it now similar to: https://learn.microsoft.com/samples/azure/azure-sdk-for-net/get-certificate-private-key/

@vtapadia
Copy link
Author

Thanks and sorry for the late response.

We are indeed looking for the Private key which was part of the key pair used to create the certificate. From the certificate, we get the key ID. Using the Key ID, I would expect that I could export the private key programatically for future usage if during creation, I marked the private key as exportable.

In general, we do not need to extract the private key and we don't either. But its a specific use case where the library depends on a file location for the keystore file. So we will really have to extract it.

I still feel that the exportable parameter value being null could indicate that this value is not set properly and might be the main reason for this not working as expected with the Java SDK but did not spend time to dive deep into the SDK.

@jlichwa
Copy link

jlichwa commented Nov 18, 2022

@vtapadia that key id is only for public key. If you need private key you need to get entire certificate in pfx or pem which is available with getSecret API. If you don't see private key when you get secret (secret download) please let us know.

"Downloading as certificate means getting the public portion. If you want both the private key and public metadata then you can download it as secret."
https://learn.microsoft.com/en-us/azure/key-vault/certificates/how-to-export-certificate?tabs=azure-cli#export-stored-certificates
If key is marked as exportable, downloaded secret (PFX/PEM) will have private key otherwise it will have private key empty.

@vtapadia
Copy link
Author

Wouldn't it be easy to support the way I have mentioned.

At least as it is mentioned as exportable, I would expect the easiest way to get the private key. Doing this as secret would need to again load it and parse it separately.

@vtapadia
Copy link
Author

Also, is it possible to get some sample on how to read the key from the secret in Java. We tried few ways using BouncyCastle but those does not seam to work.

@jlichwa
Copy link

jlichwa commented Nov 20, 2022

@vtapadia this certificate management component for TLS certs, so windows cert manager, NGiNX etc ... all cert managers requires entire certificate in pfx or pem format and API is built for that scenario. I would recommend use openssl or equivalent for any custom operations.

@vtapadia
Copy link
Author

Hi @jlichwa
I would argue that even the usage for TLS is not complete. I would be divulging from the original question but might be good to mention here.
I could not find a way to upload a certificate with our own CA to be uploaded to the Certificate for Truststore (without Private Key) use case. It fails when we try to upload a certificate stating that we also need private key.
Also, when are using it as a keystore with our client certificate, it seems it is not sending the complete chain. We are investigating this issue as well.

Maybe you have solutions or hints for these as well. Otherwise I will raise another issue for those as well.

@jlichwa
Copy link

jlichwa commented Nov 21, 2022

@vtapadia I asked to understand your use case. Today our use case is either download entire cert with private key or without for TLS offload - which does not scale well. This is to answer why our API is designed that way.
So getting just private key without certificate is likely that you are using for some specific scenario, which maybe something we did not account for.

Other than that, the solution stays, you will either do in code like in c# example (we don't have example for Java) I provided https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/keyvault/samples/getcert/Program.cs#L100
Third-party tools like openssl should also help.

For you TLS issue, yes we require to import certificate with private key. Separate issue/question has to be created.

@vtapadia
Copy link
Author

Ok, clear. I did some investigation and made some small code to get a sample setup showing the working in Java.
Might be useful for others also who run into similar issue.

// Reading of a certificate in memory from the Key Vault
//Create Identity Credentials
char[] NO_PASSWORD = "".toCharArray();
ClientSecretCredential credential = new ClientSecretCredentialBuilder()
        .tenantId(tenantId).clientId(clientId).clientSecret(clientSecret)
        .build();
 
//Retrieve the certificate with the name "keyId"
CertificateClient certificateClient = new CertificateClientBuilder()
        .vaultUrl(kvURL)
        .credential(credential)
        .buildClient();
KeyVaultCertificateWithPolicy clientCertificate = certificateClient.getCertificate(keyId);
 
log.info("certificate {}", clientCertificate);
 
//Retrieve the certificate as a secret value
SecretClient secretClient = new SecretClientBuilder()
        .vaultUrl(kvURL)
        .credential(credential)
        .buildClient();
log.info("Secret id {}", clientCertificate.getSecretId());
KeyVaultSecret secret = secretClient.getSecret(keyId);
String secretValue = secret.getValue();
byte[] decodedSecretValue = Base64.decode(secretValue);
log.trace("Secret value {}", secretValue);
 
log.info("Secret value content type {}", secret.getProperties().getContentType());
 
//Validate the content type to be pkcs12 (application/x-pkcs12)
if (secret.getProperties().getContentType().contains("pkcs12")) {
    //Create a keystore in memory to parse the content
    KeyStore store = KeyStore.getInstance("PKCS12");
    //NOTE: remember to use the base64 decoded value in this.
    store.load(new ByteArrayInputStream(decodedSecretValue), NO_PASSWORD);
 
    //Retrieve the key. This would only work if the key is marked as exportable when creating the certificate
    //Otherwise this would return null.
    Key key = store.getKey(store.aliases().nextElement(), NO_PASSWORD);
    log.info("Key found :: {}", key);
 
    //Retrieve certificate chain
    Certificate[] certificateChain = store.getCertificateChain(store.aliases().nextElement());
    log.info("Certificate chain length {}", certificateChain.length);
 
    // JKS File creation, only if required
    boolean useCaseToGenerateFile = false;
    if (useCaseToGenerateFile) {
        KeyStore fileStore = KeyStore.getInstance("JKS");
        fileStore.load(null, NO_PASSWORD);
 
        //Add the key and the certificate chain extracted earlier
        fileStore.setKeyEntry(keyId, key, NO_PASSWORD, certificateChain);
 
        //JKS File path..and store
        try (FileOutputStream fos = new FileOutputStream(keyId+".jks")) {
            fileStore.store(fos, NO_PASSWORD);
        }
    }
}

@vtapadia
Copy link
Author

Also, would it be an idea to not consider this as a bug, but as a feature request.

@heaths heaths added the feature-request This issue requires a new behavior in the product in order be resolved. label Nov 23, 2022
@ghost ghost added the needs-team-attention This issue needs attention from Azure service team or SDK team label Nov 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. feature-request This issue requires a new behavior in the product in order be resolved. KeyVault needs-team-attention This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that
Projects
None yet
Development

No branches or pull requests

5 participants