I want to connect to get Key Vault secrets using Azure AD client certificate
The example
https://github.com/Azure-Samples/key-vault-java-certificate-authentication
is not working for me.
Scenario steps:
1.Registered application in Azure AD, Added API/Permission name - Azure Key Vault
Selected user_impersonation. Have full access to Key Vault Service
2.Created certificate - pfx file
Java code from example - specifying client id (registered application client id), pfx file password, pfx file location, key vault url
Question: How does Azure know about the certificate? Never worked with pfx file. Do I need to upload certificate (it says public key .cer/pem/crt)
Can I ask what step I'm missing, as I think authentication is not happening?
java.lang.ClassCastException: class java.lang.String cannot be cast to
class java.util.List (java.lang.String and java.util.List are in
module java.base of loader 'bootstrap') at
com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils.getFirstValue(MultivaluedMapUtils.java:70)
at
com.nimbusds.oauth2.sdk.auth.JWTAuthentication.ensureClientAssertionType(JWTAuthentication.java:246)
Correction:
My application is not in the Azure VM. It is on-premise
Java Code:
I updated the github example code with the below, but the error is same on acquireAccessToken call
AzureAdTokenCredentials credentials = new AzureAdTokenCredentials(
tenant,
AsymmetricKeyCredential.create(clientId, privateKey, certificateKey.getCertificate()),
AzureEnvironments.AZURE_CLOUD_ENVIRONMENT);
TokenProvider provider = new AzureAdTokenProvider(credentials, executorService);
String newToken = provider.acquireAccessToken().getAccessToken();
java.lang.ClassCastException: class java.lang.String cannot be cast to
class java.util.List (java.lang.String and java.util.List are in
module java.base of loader 'bootstrap') at
com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils.getFirstValue(MultivaluedMapUtils.java:70)
at
com.nimbusds.oauth2.sdk.auth.JWTAuthentication.ensureClientAssertionType(JWTAuthentication.java:246)
at
com.nimbusds.oauth2.sdk.auth.PrivateKeyJWT.parse(PrivateKeyJWT.java:277)
at
com.microsoft.aad.adal4j.AuthenticationContext.createClientAuthFromClientAssertion(AuthenticationContext.java:903)
More Updates:
I see the above error in https://github.com/microsoft/azure-spring-boot/issues/457
and so updated a few of my dependencies and crossed that bridge.
New Error:
So I see it is unauthorized? and so too many follow up requests.
How I can fix this? Should I use Handling Authentication in Okhttp ? Is this only a cap to avoid error, or guarantee authentication
Regarding the issue, please refer to the following steps
Convert pfx to cer with openssl
openssl pkcs12 -in <> -out <> -nodes
Upload the cert to Azure AD application
Configure access policy for the application in Azure key vault
run the sample.
Damn it. I thought AzureAdTokenCredentials will help. Instead that had a side effect.
I rolled back my code update after seeing the error in postman
"message": "AKV10022: Invalid audience. Expected
https://vault.azure.net, found: https://rest.media.azure.net."
AsymmetricKeyCredential asymmetricKeyCredential = AsymmetricKeyCredential.create(clientId,
privateKey, certificateKey.getCertificate());
AuthenticationResult result = context.acquireToken(resource,
asymmetricKeyCredential, null).get();
String newToken = result.getAccessToken();
So this code is surely setting scope, which can be seen in the token
"aud": "https://vault.azure.net",
I followed the article - https://goodworkaround.com/2020/07/07/authenticating-to-azure-ad-as-an-application-using-certificate-based-client-credential-grant/
If anyone is not comfortable with openssl commands etc, then create/generate certificate in Key vault, Download cer / crt from there itself and import to Azure AD registeredApp
Related
I have an Azure app service I did not create but now maintain. The app service finds a certificate in a Key Vault by thumbprint and in turn uses that to get a token for doing some SQL work via nightly jobs.
It appears the certificate was set to auto renew after 80% of its valid date (12 months). The day the cert renewed my nightly jobs started to fail. I'm reasonably certain the new certificate is at the root of the problem.
As best I can tell it designed to work like this:
Job fires via Azure Logic App
annomyous POST to a reports processing API (end result should be .PDF report creation for email atachment)
API has Appsetting.json that contains the current certificates thumbprint
Thumbprint is used in the line of code below to find the certificate in the cert store
Cert is used to aquire access token and perform work
When I install both the old certificate and the new certificate on my local machine and run the entire process it works find with the old certificate and fails on this line with the new auto-generated certificate. It also fails with any new certificates I try to make in Azure and export from Azure and import to my dev machine. I've double/triple checked the appsettings to make sure the Thumbprint in question is correct and updated.
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
When this was configured a year ago the process was to go into App Service, TLS/SSL setting blade, select Private Key Certificates (.pfx) and finally + Import Key Vault Certificate.
From there you selected the Key Vault and Certificate, then changed the Appsettings.Json to have the new Thumbprint.
Why will it work with the old (soon to expire) certificate and corresponding Thumbprint entry into appsettings but fails to work with any newly created certificates and corresponding correct Thumbprint entry?
I've looked at the Configuration for the App Service in question and it has the following setting when I understand is supposed to let the app service see all certificates registered to it, right?
WEBSITE_LOAD_CERTIFICATES = *
EDIT:
After some more testing I find that any cert I export form Azure and import on my computer will successfully iterate the cert store and find any Certificate I provide a valid Thumbprint. What it won't do is use that cert to obtain a access token. The complete code is below.
The certificate that is due to expire soon will get a proper access token and run the rest of the process by getting the proper data from the DB.
The exception I get with all the other certificates suggest something about base64 encoding but I can't quite figure that out. Any ideas?
Exception for all but the original certificate:
Client assertion contains an invalid signature
Successful Access token with this code only with original certificate:
private async Task<string> GetDatabaseTokenFromCert()
{
X509Certificate2 cert;
var store = new X509Store(StoreLocation.CurrentUser);
var authContext = new AuthenticationContext(_appSettings.AzureAD.AADInstance + _appSettings.AzureAD.TenantId);
try
{
store.Open(OpenFlags.ReadOnly);
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
if (signingCert == null)
{
throw new FileNotFoundException("Cannot locate certificate for DB access!", _appSettings.AzureAD.CertificateThumbprint);
}
cert = signingCert;
}
finally
{
store.Close();
}
var certCred = new ClientAssertionCertificate(_appSettings.AzureAD.ClientId, cert);
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));
return result?.AccessToken;
}
Turns out you need to also add the new certificate to the app registration. As a .cer file without the private key, obviously.
So if you get the error message:
AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: 'YourNewCert'
Go to Key Vault, export new cert as .cer file and import it into the App Service that is trying to obtain the Access token from AcquireTokenAsync
In my case the order of operation is:
Logic app fires off anonymous call as a POST to web API
Web API uses Thumbprint of Cert in question via appsetting.json
Finds cert with thumbprint that is in App Registration of the web API
AcquireTokenAsync takes Azure info and Cert and returns Access Token
This will work or fail on this line of original post
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));
I got a node.js application and I'm trying to use the AdalFetchClient of PnPjs to fetch some data from sharepoint.
sp.setup({
sp: {
baseUrl: "https://placeholder.sharepoint.com",
fetchClientFactory: () => {
return new AdalFetchClient("tenantId", "azure_clientId", "azure_clientSecret");
},
},
});
await sp.web.getAppCatalog().get();
I get this error: Error making HttpClient request in queryable [401] Unauthorized ::> {"error_description":"Invalid issuer or signature."}
I setup the permissions of my azure active directory app like so:
Azure App permissions
I granted all the permissions to the tenant I'm trying to fetch data from:
Granted permissions to Azure App
The example I used is here: https://pnp.github.io/pnpjs/nodejs/adal-fetch-client/
I also tried to use the AdalFetchClient with graph.. which is working. Only the sharepoint api seems to have a problem.
I found the solution. There is a AdalCertificateFetchClient which requires the following paramters:
Tenant-ID
Azure App Client ID
Thumbprint of your x.509 certificate
The private key of your x.509 certificate
The root url of the sharepoint you want to connect to
So first of all you have to create a x.509 certificate. I used this tutorial for this. (Thanks for that)
After that you have to get your thumbprint by installing the certificate to your local machine and following this steps
Last step is to get your private key of your certificate. For that you have to install openssl for windows and follow this steps
Now you can use your AdalCertificateFetchClient
at the moment I´m working in azure with azure automation and automation account. For executing a runbook I want to authenticate me with a service principal + certificate.
Unfortunately I get the error message "The private key is not present in the X.509 certificate".
I will use:
Add-AzureRmAccount -ServicePrincipal -Tenant xxx -ApplicationId xxx -CertificateThumbprint xxx
But with Login-AzureRmAccount and Connect-AzureRmAccount I get the same error message.
What have I done so far?
Since I´m working in a big company I can´t use a self-signed certificate. Our team have created a .cer-File and .key (Private Key) file for me. After some testing I find out that I need something like this:
Example
Furthermore, I know that I can get this with an .pfx file but this is not accepted from the other team, which will import the certificate to my service principal since they only accept .cer files.
How can I get a .cer file with an public key included?
Thanks a lot!
Your certificate needs to include the private key if you want to sign in with it, which from the error message appears missing. A certificate with the private key included should have a file name of ~.pfx. For reference, check the MS doco here: "Clients which sign in with the service principal also need access to the certificate's private key"
I've uploaded a pfx certificate as a secret to my Azure Portal and I now want to use this to sign the credentials in IdentityServer4.
I've got reference to the vault in the startup of the api using:
builder.AddAzureKeyVault(
$"https://{config["Vault"]}.vault.azure.net/",
config["ClientId"],
config["ClientSecret"]);
But not too sure how to get the certificate out to pass to:
services.AddIdentityServer()
.AddSigningCredential(...)
Is it possibly to be able to reference the certificate directly from the key vault, or do I need to deploy the cert to the web app the api is running on?
Thanks
You are already building the IConfiguration object, so you can reference the pfx key just like you would reference any other object from the configuration.
If the key is in the keyvault you can reference it something like:
// Name of your pfx in the keyvault.
var key = _configuration["pfx"];
var pfxBytes = Convert.FromBase64String(key);
// Create the certificate.
var cert = new X509Certificate2(pfxBytes);
services.AddIdentityServer()
.AddSigningCredential(cert);
I would recommend using the keyvault for this but you could decide to upload the pfx to the certificate store of the web app. You can do this in Azure by going to your web app -> SSL Certificates -> Upload certificate and enter the password. Go to the Application Settings and add the app setting WEBSITE_LOAD_CERTIFICATES : <thumbprint>. The last thing you would do is retrieve the certificate from the store and add it again like AddSigningCredential(cert);.
I'am searching for public/private key-based authentication for users with Azure-ActiveDirectory but can't find any hints in Azure AD Authentification Scenarios. Every user should bring his own key pair. Any suggestions how to achieve it?
Azure AD supports OAuth 2.0 to authorize the third-party apps and the OAuth 2.0 support to acquire the token using the app’s client credential.
There are two ways to acquire the token for the client credential flow.
First is that using the keys which generated by the Azure portal like figure below:
And here is a figure about token request using the key(client_secret):
Another way is using the cert. Technical speaking, the cert is a pair of public/private key. We will store the information of public key with the app. When we require to prove we are the owner of the third-party apps, we need to sign the message with the private key and Azure AD with verify the messages with public key.
To store the cert information with apps, we need to change the manifest of app. Here is the detail steps to use a self-signed certificate from here:
1.Generate a self-signed certificate:
makecert -r -pe -n "CN=MyCompanyName MyAppName Cert" -b 03/15/2015 -e 03/15/2017 -ss my -len 2048
2.Open the Certificates MMC snap-in and connect to your user account.
3.Find the new certificate in the Personal folder and export the public key to a base64-encoded file (for example, mycompanyname.cer). Your application will use this certificate to communicate with AAD, so make sure you retain access to the private key as well.
Note You can use Windows PowerShell to extract the thumbprint and base64-encoded public key. Other platforms provide similar tools to retrieve properties of certificates.
4.From the Windows PowerShell prompt, type and run the following:
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("mycer.cer")
$bin = $cer.GetRawCertData()
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
$keyid = [System.Guid]::NewGuid().ToString()
5.Store the values for $base64Thumbprint, $base64Value and $keyid, to be used when you update your application manifest in the next set of steps.
Using the values extracted from the certificate and the generated key ID, you must now update your application manifest in Azure AD.
6.In the Azure Management Portal, select your application and choose Configure in the top menu.
7.In the command bar, click Manage manifest and select Download Manifest.
8.Open the downloaded manifest for editing and replace the empty KeyCredentials property with the following JSON:
"keyCredentials": [
{
"customKeyIdentifier": "$base64Thumbprint_from_above",
"keyId": "$keyid_from_above",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"value": "$base64Value_from_above"
}
],
9.Save your changes and upload the updated manifest by clicking Manage manifest in the command bar, selectingUpload manifest, browsing to your updated manifest file, and then selecting it.
And below is the figure about token request using the cert:
In the article above, it generate the client_assertion from scratch. We can also use the ADAL library to help us and authenticate for a daemon apps:
string authority = $"https://login.microsoftonline.com/{tenant}";
var thumbprint="";
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint, thumbprint, false);
X509Certificate2 cert = fcollection[0];
var certCred = new ClientAssertionCertificate(clientId, cert);
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync(resource, certCred);
}
catch (Exception ex)
{
}
return result.AccessToken;
And if you want to use the cert to authorize for the OAuth2.0 code grant flow, you also can refer the code sample here.
If you're looking for a programmatic method to authenticate users (not apps) in Azure AD using certificates, this is not currently possible.
It is possible to do certificate-based authentication in AD FS (e.g.), and it is possible to federate authentication for users from Azure AD to AD FS. However, this requires the overhead of Windows Server AD, Azure AD Connect and AD FS, and I don't think the federated authentication can be achieved in a programmatic way (i.e. without prompting the user to choose a certificate).