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] Use key vault certificate to use as authentication source in Spring/webflux WebClient #32854

Open
1 task done
rmshnair opened this issue Jan 10, 2023 · 8 comments
Open
1 task done
Assignees
Labels
azure-spring All azure-spring related issues azure-spring-jca azure-spring-keyvault Spring keyvault related issues. bug This issue requires a change to an existing behavior in the product in order to be resolved. 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. needs-team-attention This issue needs attention from Azure service team or SDK team

Comments

@rmshnair
Copy link

Query/Question
unable to choose the alias key for keymanager for the request causing authentication failure on requests thats uses client certificate for authentication/authorization.

AzureKeyVaultKeyManager always returns the first alias it found (which is from JVM Keystore)

Why is this not a Bug or a feature Request?
A clear explanation of why is this not a bug or a feature request?

Setup (please complete the following information if applicable):

  • OS: windows
  • IDE: IntelliJ
  • Library/Libraries:

    com.azure.spring
    azure-spring-boot-starter-keyvault-certificates
    3.14.0

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

  • [x ] Query 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 Jan 10, 2023
@joshfree joshfree added azure-spring All azure-spring related issues azure-spring-keyvault Spring keyvault related issues. labels Jan 10, 2023
@ghost ghost removed the needs-triage This is a new issue that needs to be triaged to the appropriate team. label Jan 10, 2023
@joshfree joshfree added Client This issue points to a problem in the data-plane of the library. needs-triage This is a new issue that needs to be triaged to the appropriate team. labels Jan 10, 2023
@ghost ghost removed the needs-triage This is a new issue that needs to be triaged to the appropriate team. label Jan 10, 2023
@joshfree
Copy link
Member

@backwind1233 could you please follow up with @rmshnair on this github issue?

@backwind1233
Copy link
Contributor

Hi @rmshnair, thanks for reaching out, could you add more context about your issue scenario?
I am not quite sure about your problem, you want to choose the alias of keyvault certificates?
could you check this link, does it meet your needs?

@backwind1233 backwind1233 modified the milestone: 2023-02 Jan 11, 2023
@rmshnair
Copy link
Author

rmshnair commented Jan 11, 2023

@backwind1233, i looked at the link that you have provided. if i understand correctly, the configuration is meant to select a certificate from the vault to secure the server with SSL and additionally, we could use for mutual authentication on channel creation and trust anchor for validating client certificate.

My scenario is, my client is trying to connect to a server and add a chosen alias from the keyvault part of HTTP packets as client certificate, so that server can read the key from Request.ClientCertificate to authenticate and authorize my client to perform the requested api function.

in order to do this, i do following code to create a SSLContext when i work with Apache/RestTemplate.

//SSLContext creation for RestTemplate (org.apache)
SSLContexts.custom()
                                        .loadKeyMaterial(azureKeyVaultKeyStore, null,
                                                (map, socket) -> "myalias")
                                        .build()

Note: i noticed that the like that you have provided has an example to cover the above scenario for RestTemplate.

however, with webclient uses, Keymanager apis, i do not have an method to choose the alias.

//SslContext creation for WebClient (io.reactor)
SslContext sslContext = SslContextBuilder.forClient()
                                        .keyManager(manager)
                                        .build();

Please note, if i just add trustmanager, channel will be established, but no certificate will be added to the http request, hence the authentication fails with 401 error.

is there any similar configuration available to select the alias on keymanager.

@rmshnair
Copy link
Author

Also wondering about what was the reason behind on below sceanrios

  1. keyvaultKeystore is loading the local JVM keystore certificates. i understand the reason. but wondering why it is added prior to KV certificate.
  2. KeyvaultKeymanager returns null as alias if the provider is AzureKeyVault
  3. KeyvaultKeymanager returns first alias name it finds as Client alias irrespective of inputs (always)

@backwind1233
Copy link
Contributor

backwind1233 commented Jan 16, 2023

@backwind1233, i looked at the link that you have provided. if i understand correctly, the configuration is meant to select a certificate from the vault to secure the server with SSL and additionally, we could use for mutual authentication on channel creation and trust anchor for validating client certificate.

Yes, you are right.

My scenario is, my client is trying to connect to a server and add a chosen alias from the keyvault part of HTTP packets as client certificate, so that server can read the key from Request.ClientCertificate to authenticate and authorize my client to perform the requested api function.

If i understand correctly , you want to create mutual Tls connections.

in order to do this, i do following code to create a SSLContext when i work with Apache/RestTemplate.

//SSLContext creation for RestTemplate (org.apache)
SSLContexts.custom()
                                        .loadKeyMaterial(azureKeyVaultKeyStore, null,
                                                (map, socket) -> "myalias")
                                        .build()

Note: i noticed that the like that you have provided has an example to cover the above scenario for RestTemplate.

however, with webclient uses, Keymanager apis, i do not have an method to choose the alias.

//SslContext creation for WebClient (io.reactor)
SslContext sslContext = SslContextBuilder.forClient()
                                        .keyManager(manager)
                                        .build();

Please note, if i just add trustmanager, channel will be established, but no certificate will be added to the http request, hence the authentication fails with 401 error.

is there any similar configuration available to select the alias on keymanager.

Hi @rmshnair
I think there is a workaround you can refer.
I create a webclient to use the alias, you may try to refer this code here.
You can find both restTemplate and webclient configurations in the two different files: SampleApplicationConfiguration.java and WebClientApplicationConfiguration.java

I have tested in my machine, so it should work.
image

Please note the workaround, you need add a new class ConfigurableAliasKeyManager.

public WebClient webClientWithMTLS() throws Exception {
    KeyStore azureKeyVaultKeyStore = buildAzureKeyVaultKeyStore();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(azureKeyVaultKeyStore);

    KeyManager keyManager = buildKeyManager(azureKeyVaultKeyStore, ALIAS);

    SslContext context = SslContextBuilder.forClient()
                                          .keyManager(keyManager)
                                          .trustManager(trustManagerFactory)
                                          .build();

    HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(context));

    return WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .build();
}

private static KeyManager buildKeyManager(KeyStore azureKeyVaultKeyStore, String alias) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(azureKeyVaultKeyStore, "".toCharArray());
    for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
        if (keyManager instanceof KeyVaultKeyManager) {
            return new ConfigurableAliasKeyManager((KeyVaultKeyManager) keyManager, alias);
        }
    }
    return null;
}

@rmshnair
Copy link
Author

Thanks for the thoughts. i am trying the first method, while i have implemented the second method already before i raise this request.

@backwind1233
Copy link
Contributor

backwind1233 commented Jan 17, 2023

Context

KeyVaultKeyManager is a class extends X509ExtendedKeyManager in azure-security-keyvault-jca, it has several apis, and there are 4 apis' implementations in the KeyVaultKeyManager need to be reconsidered.

public String chooseClientAlias(String[] keyType, Principal[] issuers,
      Socket socket);

public String chooseServerAlias(String keyType, Principal[] issuers,
    Socket socket);

    /**
     * Choose an alias to authenticate the client side of an
     * <code>SSLEngine</code> connection given the public key type
     * and the list of certificate issuer authorities recognized by
     * the peer (if any).
    / * 
public String chooseEngineClientAlias(String[] keyType,
          Principal[] issuers, SSLEngine engine);

   /**
     * Choose an alias to authenticate the server side of an
     * <code>SSLEngine</code> connection given the public key type
     * and the list of certificate issuer authorities recognized by
     * the peer (if any).
     * /
public String chooseEngineServerAlias(String keyType,
            Principal[] issuers, SSLEngine engine);

Problem

  1. Currently, the chooseClientAlias and chooseServerAlias will always return null with KeyVaultKeyStore.
  2. chooseEngineClientAlias and chooseEngineServerAlias have default implementations return null, we need to implement the logic using KeyVault.

Goal

  1. Fix logic in chooseClientAlias chooseServerAlias
  2. Follow the java doc to implement the logic in chooseEngineClientAlias and chooseEngineServerAlias.

@backwind1233
Copy link
Contributor

Hi @rmshnair, while investigating your issue, we found some other issues in our library, since it's all related with enabling tls/mTls with keyVault JCA, we would like to use your issue to track the issues, you may also get the updates.

@stliu stliu added bug This issue requires a change to an existing behavior in the product in order to be resolved. and removed question The issue doesn't require a change to the product in order to be resolved. Most issues start as that labels Jan 19, 2023
@stliu stliu changed the title [QUERY] Use key vault certificate to use as authentication source in Spring/webflux WebClient [Bug] Use key vault certificate to use as authentication source in Spring/webflux WebClient Jan 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
azure-spring All azure-spring related issues azure-spring-jca azure-spring-keyvault Spring keyvault related issues. bug This issue requires a change to an existing behavior in the product in order to be resolved. 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. needs-team-attention This issue needs attention from Azure service team or SDK team
Projects
Status: Todo
Development

No branches or pull requests

4 participants