Hot questions for Using Azure in azure keyvault

Top Java Programmings / Azure / azure keyvault

Question:

In my previous question i tried to access the Azure Key Vault using clientID and client-secret and get the vault information. Now am trying to access the key vault using clientID and certificate based authentication. I have googled and found this. But unable to proceed further, as i could not understand how to proceed further.

ApplicationTokenCredentials credentials = new ApplicationTokenCredentials(
        client, tenant, pfxCertificatePath, password, AzureEnvironment.AZURE);
Azure azure = Azure.authenticate(credentials).withSubscription(subscriptionId);

Could someone provide insights what should be the value for tenant and subscriptioID. Also, do i have to provide my certification path in the pfxcertification Please help me in understanding the above lines of code.

Thanks


Answer:

This link will be helpful.

Could someone provide insights what should be the value for tenant and subscriptioID.

If my understanding is right, tenant value is your domain name or tenant id. The subscription id is the subscription that you want to manage key vault.

You could create a auth file like below.

subscription=########-####-####-####-############
client=########-####-####-####-############
tenant=########-####-####-####-############
certificate=<path to certificate file>
certificatePassword=XXXXXXXXXXXXXXXX
managementURI=https\://management.core.windows.net/
baseURL=https\://management.azure.com/
authURL=https\://login.windows.net/
graphURL=https\://graph.windows.net/

Question:

I'm running AzureClient java sdk. I create keyvault client like this:

ApplicationTokenCredentials applicationTokenCredentials=new 
ApplicationTokenCredentials(APPLICATION_ID, "DOMAIN", CLIENT_SECRET, 
AzureEnvironment.AZURE);
vc = new KeyVaultClient(applicationTokenCredentials);

And the i write this code to get key from azure directory:

Future<KeyBundle> keyBundleFuture = vc.getKeyAsync(testKeyIdentifier, new ServiceCallback<KeyBundle>() {
    public void failure(Throwable throwable) {

    }

    public void success(KeyBundle keyBundle) {
        System.out.print(keyBundle.toString());
    }
});
KeyBundle keyBundle = keyBundleFuture.get();

But i'm getting this error

Exception in thread "main" java.util.concurrent.ExecutionException: com.microsoft.azure.keyvault.models.KeyVaultErrorException: Status code 401.

Also to note that I have given permissions to my applocation from azure portal to access keyvault


Answer:

According to the status code 401 of your error and the REST API reference Authentication, requests, and responses of Key Vault, it was caused by using incorrect credentials with Azure Java SDK. To access Key Vault using Azure SDK must be authenticated with KeyVaultCredentials which need to be implemented the method doAuthenticate.

As reference, here is my sample code below.

ServiceClientCredentials credentials = new KeyVaultCredentials() {

    @Override
    public String doAuthenticate(String authorization, String resource, String scope) {
        AuthenticationResult res = null;

        try {
            res = GetAccessToken(authorization, resource, clientId, secret);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            return res.getAccessToken();
    }

    private AuthenticationResult GetAccessToken(String authorization, String resource, String clientID, String clientKey)
            throws InterruptedException, ExecutionException {
        AuthenticationContext ctx = null;
        ExecutorService service = Executors.newFixedThreadPool(1);
        try {
            ctx = new AuthenticationContext(authorization, false, service);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Future<AuthenticationResult> resp = ctx.acquireToken(resource, new ClientCredential(
            clientID, clientKey), null);
            AuthenticationResult res = resp.get();
            return res;
        }

    };
KeyVaultClient client = new KeyVaultClient(credentials);
String keyIdentifier = "https://<your-keyvault>.vault.azure.net/keys/<your-key>/xxxxxxxxxxxxxxxxxxxxxx";
KeyBundle keyBundle = client.getKey(keyIdentifier);

Then, it works.

Question:

I am trying to connect to Azure KeyVault from my locally running Spring Boot Application. I can't keep those secrets to be saved in keyvault in different properties or yaml during dev, because my application will generate and delete so many secrets and tokens to be saved in keyvault in the run time.

I am aware of the process in which we can create an Azure service principal from your application registration. And use

azure.keyvault.client-id azure.keyvault.client-key

in application.properties to connect.

But it may not be allowed to be created Azure service principal in our case. So is there any way to connect to key vault using MSI from locally running SpringBoot application.

using MSI_ENDPOINT and MSI_SECRET


Answer:

So is there any way to connect to key vault using MSI from locally running SpringBoot application. using MSI_ENDPOINT and MSI_SECRET

I don't think you can use MSI_ENDPOINT and MSI_SECRET get the token in local, it just works when the web app published in the cloud.

But it may not be allowed to be created Azure service principal in our case.

As you know, you can use the service principal client id and secret(key) to access the keyvault. Actually, when enabling the MSI of the web app, it will create a service principal in your Azure AD tenant automatically. So you can just use the client id and secret of it.

Navigate to the portal -> Azure Active Directory -> Enterprise applications -> search for your web app name(select the Application Type with All Applications), then you get the client id(application id).

Note: Remember to check the object id of the service principal with that in your web app -> Identity, make sure you use the correct one.

For the service principal secret, you could create it via powershell like below(your account need the admin role Application administrator or Global administrator in your AAD tenant).

New-AzureADServicePrincipalPasswordCredential -ObjectId <service principal object id>

Then you will be able to access the keyvault with the client id and secret. For details in java, you can refer to this link.

Question:

I want to get secret from Azure key vault.

I found codes below and tried it. But I failed with error.

    private String clientId= '<I put my client Id here>';
    private String secret= '<I put my client secret here>';



KeyVaultClient client = new KeyVaultClient(credentials);

String secret = client.getSecret("https://<myVault>.vault.azure.net", "secret name").value();
        log.debug("secret=============",secret);
    }


    ServiceClientCredentials credentials = new KeyVaultCredentials() {

        @Override
        public String doAuthenticate(String authorization, String resource, String scope) {
            AuthenticationResult res = null;

            try {
                res = GetAccessToken(authorization, resource, clientId, secret);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
                return res.getAccessToken();
        }

        private AuthenticationResult GetAccessToken(String authorization, String resource, String clientID, String clientKey)
                throws InterruptedException, ExecutionException {
            AuthenticationContext ctx = null;
            ExecutorService service = Executors.newFixedThreadPool(1);
            try {
                ctx = new AuthenticationContext(authorization, false, service);
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Future<AuthenticationResult> resp = ctx.acquireToken(resource, new ClientCredential(
                clientID, clientKey), null);
                AuthenticationResult res = resp.get();
                return res;
            }

I got error like below:

[http-nio-8080-exec-1] ERROR c.t.c.e.GlobalExceptionHandler - Error >>> java.net.ConnectException: Failed to connect

How can i get secret from key vault? Is there anything i should do more?

Thank you.


Answer:

It seems that you want to access the azure key vault with application.

  1. Register a web app in Azure AD

  2. You can get the client id (application id) at the overview

  3. Add a secret

  4. Assign access policy in key vault

  5. Save the policy, so that it will take effect.

  6. Code sample

public class KeyVaultTest {

    private static AuthenticationResult getAccessToken(String authorization, String resource) throws InterruptedException, ExecutionException, MalformedURLException {

        String clientId = "dc17****-****-****-****-ea03****a5e7"; // Client ID
        String clientKey = "1YWt******k21";  //Client Secret

        AuthenticationResult result = null;

        //Starts a service to fetch access token.
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            AuthenticationContext context = new AuthenticationContext(authorization, false, service);

            Future<AuthenticationResult> future = null;

            //Acquires token based on client ID and client secret.
            if (clientKey != null && clientKey != null) {
                ClientCredential credentials = new ClientCredential(clientId, clientKey);
                future = context.acquireToken(resource, credentials, null);
            }

            result = future.get();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new RuntimeException("Authentication results were null.");
        }
        return result;
    }

    public static void main(String[] args) {
        String vaultBase = "https://jackkv.vault.azure.net/";

        KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultCredentials(){
            @Override
            public String doAuthenticate(String authorization, String resource, String scope) {
                String token = null;
                try {
                    AuthenticationResult authResult = getAccessToken(authorization, resource);
                    token = authResult.getAccessToken();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return token;
            }
        });

        SecretBundle test = keyVaultClient.getSecret(vaultBase, "test");
        System.out.println(test.value());
    }
}


Update:

If you face connection issues, please check if you have set the firewall for your key vault.

If you set the firewall, please add your IP to the allowed list:

Question:

In azure Keyvault website link

I found this code:

KeyVaultClient kvc = new KeyVaultClient(credentials);
KeyBundle returnedKeyBundle = getKey(vaultUrl, keyName);
JsonWebKey jsonKey = returnedKeyBundle.key();

But how to get he client credentials is not explained, and all the old methods is not valid with the library version 1.0.0. I want to use application id and secret to authenticate, but how?


Answer:

Per my understanding, I think the code below is that you want if you had known how to register an application in Azure AD.

String clientId = "<client id of your application registed on Azure AD>";
String domain = "<your talnet id>";
String secret = "<client key of your application registed on Azure AD>";
String subscription = "<your subscription id>";
AzureTokenCredentials cred = new ApplicationTokenCredentials(clientId, domain, secret, AzureEnvironment.AZURE);
KeyVaultClient kvc = new KeyVaultClient(credentials);

Or follow the figure below to register an application in Azure AD on Azure portal, then to do the above.


Update: Please add the azure dependency as below

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure</artifactId>
    <version>1.1.0</version>
</dependency>

Question:

I am trying to create a KeyVaultClient object from the Azure Java SDK from a pfx certificate. I found a clear document on how to do that in C# but have to clue how to do it in Java.

I am able to get a Windows certificate from the user account keystore, but I do not really know what to pass to the KeyVaultClient constructor. It looks like it accepts object of type TokenCredentials, but I cannot find any document on how to actually build one of those (which needs a "token" and a "scheme").


Answer:

It seems I am close to have something working by defining this class:

  class WindowsStoreCertificateCredentials(clientId: String, certificate: X509Certificate, privateKey: PrivateKey) extends KeyVaultCredentials {

def getAuthResult(authority: String, resource: String): AuthenticationResult  = {       
  val service  = Executors.newFixedThreadPool(1)
  val context = new AuthenticationContext(authority, false, service)

  val certificateCredentials = AsymmetricKeyCredential.create(clientId, privateKey, certificate)
  val authResultFuture = context.acquireToken(resource, certificateCredentials, null)
  authResultFuture.get
}

override def doAuthenticate (authority: String, resource: String, scope: String): String = {
  getAuthResult(authority, resource).getAccessToken
}

}

And trying to use it after getting a certificate X509Certificate object and private key using java.security.KeyStore:

val client = new KeyVaultClient(new WindowsStoreCertificateCredentials(
  id, privateKey, certificate,))

val test = client.getSecret("https:/...")

Unfortunately it raises an exception:

sun.security.mscapi.RSAPrivateKey cannot be cast to java.security.interfaces.RSAPrivateKey java.lang.ClassCastException:  sun.security.mscapi.RSAPrivateKey cannot be cast to java.security.interfaces.RSAPrivateKey

I opened an issue on github AzureAD/azure-activedirectory-library-for-java and proposed a pull request to fix it, to be continued...

Edit: this is now fixed in version 1.2.0 of AzureAD/azure-activedirectory-library-for-java.