how to configure a certificate to invoke a HTTPS end point using HTTP POST action API application - azure

I am new to Azure development and want some help.
I have a HTTPS end point of a web service from my third party.
The webservice call uses a client certificate and user name and password for invoking the SOAP action.
I am using a default HTTP action API and passing the URL, headers, authentication and body as required.
How can i set the certificate setting there ??
Any help or way forward would be great help.Thanks in advance

This is an example for Java, you should find the right implementation for the language that you use.
public static void main(String[] args) throws Exception {
DefaultHttpClient httpClient = new DefaultHttpClient();
try {
SSLContext ctx = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = getTrustManagers("jks", new FileInputStream(new File("cacerts")), "changeit");
KeyManager[] keyManagers = getKeyManagers("pkcs12", new FileInputStream(new File("clientCert.pfx")), "password");
ctx.init(keyManagers, trustManagers, new SecureRandom());
SSLSocketFactory factory = new SSLSocketFactory(ctx, new StrictHostnameVerifier());
ClientConnectionManager manager = httpClient.getConnectionManager();
manager.getSchemeRegistry().register(new Scheme("https", 443, factory));
//as before
}
}
protected static KeyManager[] getKeyManagers(String keyStoreType, InputStream keyStoreFile, String keyStorePassword) throws Exception {
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
return kmf.getKeyManagers();
}
protected static TrustManager[] getTrustManagers(String trustStoreType, InputStream trustStoreFile, String trustStorePassword) throws Exception {
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
trustStore.load(trustStoreFile, trustStorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
return tmf.getTrustManagers();
}
PS: This code was provided by Barry Pitman in this link

Related

Issue related to fetching certificate from Azure Keyvault using Java

I am migrating our legacy application into Azure Cloud . In the existing application we are securing our Jetty Server while starting so for that we are using jks file to secure our Jetty server .
Now we are moving into Azure Cloud so we have to fetch the .jks file from Azure keyvault . So how to fetch the complete .jks file from Azure keyvault . I am able to fetch the secrets from Keyvault but unable to fetch the certificate (which i uploaded in Azure keyvault). I am not sure whether we have any API which gives the certificate file.
Below code i am using to fetch the secrets & certificates:
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials;
import com.microsoft.azure.keyvault.models.CertificateBundle;
import com.microsoft.azure.keyvault.models.SecretBundle;
public class DemoTest {
private static String vaultBase = "https://abc.vault.azure.net/";
private static String ClientId = "*******************";
private static String clientKey = "*****************";
public static void main(String[] args) {
KeyVaultClient keyVaultClient = GetKeyVaultClient();
SecretBundle getSecret=keyVaultClient.getSecret(vaultBase, "mysecretkey");
SecretBundle getSecret1=keyVaultClient.getSecret(vaultBase, "DB-PASSWORD-POC");
SecretBundle getSecret2=keyVaultClient.getSecret(vaultBase, "certificate-value");
// SecretBundle getSecret2=keyVaultClient.getSecret(vaultBase, "DB-PASSWORD-DEV");
CertificateBundle getCertificate=keyVaultClient.getCertificate(vaultBase, "abcprod");
CertificateBundle bundle = keyVaultClient.getCertificate("https://abc.vault.azure.net/certificates/abcprod/********386c9403bab8337ce21d27495");
System.out.println(getSecret.value());
System.out.println(getSecret1.value());
System.out.println(getSecret2.value());
// System.out.println(getCertificate.contentType());
// System.out.println(getCertificate.id());
// System.out.println(getCertificate.kid());
// System.out.println(getCertificate.toString());
// System.out.println(getCertificate.attributes().toString());
// System.out.println(getCertificate.keyIdentifier().name());
// System.out.println(getCertificate.sid());
// System.out.println(getCertificate.certificateIdentifier().baseIdentifier());
// System.out.println(bundle.cer());
// System.out.println(bundle);
}
private static KeyVaultClient GetKeyVaultClient() {
return 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;
}
});
}
public static AuthenticationResult getAccessToken(String authorization, String resource) throws InterruptedException, ExecutionException, MalformedURLException {
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 (ClientId != 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;
}
}
We are securing our jetty server with this code :
public class ABCSubscriber {
private static final int Port = 9090;
private static final String KeyStoreType = "jks";
private static final String KeyStoreFile = "/home/abc/xyz/subscriber.jks";
private static final String KeyStorePassword = "******";
private static final String KeyPassword = "*******";
private static final String ContextPath = "/";
private static final String URLPattern = "/*";
public static void main(String[] args) throws Exception {
Server server = new Server();
HttpConfiguration http_config = new HttpConfiguration();
http_config.setSecureScheme("https");
http_config.setSecurePort(Port);
http_config.setRequestHeaderSize(8192);
// HTTP connector
ServerConnector http = new ServerConnector(server,
new HttpConnectionFactory(http_config));
http.setPort(9091);
http.setIdleTimeout(30000);
// SSL Context Factory
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreType(KeyStoreType);
sslContextFactory.setKeyStorePath(KeyStoreFile);
sslContextFactory.setKeyStorePassword(KeyStorePassword);
sslContextFactory.setKeyManagerPassword(KeyPassword);
// sslContextFactory.setTrustStorePath(ncm.getKSFile());
// sslContextFactory.setTrustStorePassword("changeit");
sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA",
"SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA",
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
// SSL HTTP Configuration
HttpConfiguration https_config = new HttpConfiguration(http_config);
https_config.addCustomizer(new SecureRequestCustomizer());
// SSL Connector
ServerConnector sslConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
sslConnector.setPort(Port);
server.addConnector(sslConnector);
/**Disable and enable protocols*/
String[] includeProtocols = {"TLSv1.1","TLSv1.2"};
sslContextFactory.addExcludeProtocols("TLSv1.0");
sslContextFactory.setIncludeProtocols(includeProtocols);
/**End Disable and enable protocols*/
// HTTPS Configuration
ServerConnector https = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(https_config));
https.setPort(Port);
https.setIdleTimeout(30000);
//server.setConnectors(new Connector[] { http, https });
server.setConnectors(new Connector[] { https });
ServletContextHandler ctxt = new ServletContextHandler(0);
ctxt.setContextPath(ContextPath);
server.setHandler(ctxt);
ctxt.addServlet(new ServletHolder(new ABCServlet()), "/*");
try {
server.start();
} catch ( Exception e ) {
e.getLocalizedMessage();
};
server.join();
}
}
So , Is there any way to get the certificate file from Azure keyvault? If not then how can we use certificate to secure the server ?
Can anyone please help me on this ?
Thanks in Advance !!!
You need to download the private key of the certificate as a secret. Getting the secret using the more obvious GetCertificate will only return the public key part of the certificate.
I know this is C# in the code example below, but that is how I get the certificate out of Key Vault, I hope you can get an idea on how to do the same in Java:
/// <summary>
/// Helper method to get a certificate
///
/// Source https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs
/// </summary>
/// <param name="certificateClient"></param>
/// <param name="secretClient"></param>
/// <param name="certificateName"></param>
/// <returns></returns>
private static X509Certificate2 GetCertificateAsync(CertificateClient certificateClient,
SecretClient secretClient,
string certificateName)
{
KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);
// Return a certificate with only the public key if the private key is not exportable.
if (certificate.Policy?.Exportable != true)
{
return new X509Certificate2(certificate.Cer);
}
// Parse the secret ID and version to retrieve the private key.
string[] segments = certificate.SecretId.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length != 3)
{
throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length}, URI: {certificate.SecretId}");
}
string secretName = segments[1];
string secretVersion = segments[2];
KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);
// For PEM, you'll need to extract the base64-encoded message body.
// .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
if ("application/x-pkcs12".Equals(secret.Properties.ContentType, StringComparison.InvariantCultureIgnoreCase))
{
byte[] pfx = Convert.FromBase64String(secret.Value);
return new X509Certificate2(pfx);
}
throw new NotSupportedException($"Only PKCS#12 is supported. Found Content-Type: {secret.Properties.ContentType}");
}
}
}
Here is the Java code equivalent to answer posted by #Tore Nestenius
public byte[] getPkcsFromAzureKeyVault(String certificateName) throws InvalidDataException {
String keyVaultName = System.getenv("KEY_VAULT_NAME");
String keyVaultUri = "https://" + keyVaultName + ".vault.azure.net";
CertificateClient certificateClient = new CertificateClientBuilder().vaultUrl(keyVaultUri)
.credential(new DefaultAzureCredentialBuilder().build()).buildClient();
KeyVaultCertificateWithPolicy certPol = certificateClient.getCertificate(certificateName);
SecretClient secretClient = new SecretClientBuilder().vaultUrl(keyVaultUri)
.credential(new DefaultAzureCredentialBuilder().build()).buildClient();
KeyVaultSecret secret = secretClient.getSecret(certPol.getProperties().getName(),
certPol.getProperties().getVersion());
if ("application/x-pkcs12".equalsIgnoreCase(secret.getProperties().getContentType())) {
return Base64.getDecoder().decode(secret.getValue());
}
throw new InvalidDataException();
}

How to manually decrypt an ASP.NET Core Authentication cookie?

Let's consider a common-known ASP.NET Core scenario. Firstly we add the middleware:
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Login/"),
AccessDeniedPath = new PathString("/Home/AccessDenied/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
//...
}
Then serialize a principal:
await HttpContext.Authentication.SignInAsync("MyCookie", principal);
After these two calls an encrypted cookie will be stored at the client side. You can see the cookie (in my case it was chunked) in any browser devtools:
It's not a problem (and not a question) to work with cookies from application code.
My question is: how to decrypt the cookie outside the application? I guess a private key is needed for that, how to get it?
I checked the docs and found only common words:
This will create an encrypted cookie and add it to the current
response. The AuthenticationScheme specified during configuration must
also be used when calling SignInAsync.
Under the covers the encryption used is ASP.NET's Data Protection
system. If you are hosting on multiple machines, load balancing or
using a web farm then you will need to configure data protection to
use the same key ring and application identifier.
So, is it possible to decrypt the authentication cookie, and if so how?
UPDATE #1:
Based on Ron C great answer and comments, I've ended up with code:
public class Startup
{
//constructor is omitted...
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection().PersistKeysToFileSystem(
new DirectoryInfo(#"C:\temp-keys\"));
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Index/"),
AccessDeniedPath = new PathString("/Home/AccessDenied/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
await HttpContext.Authentication.SignInAsync("MyCookie", new ClaimsPrincipal());
return View();
}
public IActionResult DecryptCookie()
{
var provider = DataProtectionProvider.Create(new DirectoryInfo(#"C:\temp-keys\"));
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
var dataProtector = provider.CreateProtector(
typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(false, true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
return Content(plainText);
}
}
Unfortunately this code always produces exception on Unprotect method call:
CryptographicException in Microsoft.AspNetCore.DataProtection.dll:
Additional information: The payload was invalid.
I tested different variations of this code on several machines without positive result. Probably I made a mistake, but where?
UPDATE #2: My mistake was the DataProtectionProvider hasn't been set in UseCookieAuthentication. Thanks to #RonC again.
Decrypting the Authentication Cookie without needing the keys
It's worth noting that you don't need to gain access to the keys to decrypt the authentication cookie. You simply need to use the right IDataProtector created with the right purpose parameter, and subpurpose parameters.
Based on the CookieAuthenticationMiddleware source code https://github.com/aspnet/Security/blob/rel/1.1.1/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationMiddleware.cs#L4 it looks like the purpose you need to pass is typeof(CookieAuthenticationMiddleware). And since they are passing additional parameters to the IDataProtector you will need to match them. So this line of code should get you an IDataProtector that can be used to decrypt the authentication cookie:
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");
Note thatOptions.AuthenticationScheme is just "MyCookie" in this case since that's what it was set to in the Configure method of the startup.cs file.
Here is an example action method for decrypting your authentication cookie two different ways:
public IActionResult DecryptCookie() {
//Get the encrypted cookie value
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
//Get a data protector to use with either approach
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get the decrypted cookie as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue);
return View();
}
This method uses an IDataProtectionProvider called provider that is constructor injected.
Decrypting the Authentication Cookie when persisting keys to a directory
If you want to share cookies between applications then you might decide to persist the data protection keys to a directory. This can be done by adding the following to the ConfigureServices method of the startup.cs file:
services.AddDataProtection().PersistKeysToFileSystem(
new DirectoryInfo(#"C:\temp-keys\"));
BE CAREFUL though because the keys are not encrypted so it's up to you to protect them!!! Only persist the keys to a directory if you absolutely must, (or if you are just trying to understand how the system works). You will also need to specify a cookie DataProtectionProvider that uses those keys. This can be done with the help of the UseCookieAuthentication configuration in the Configure method of the startup.cs class like so:
app.UseCookieAuthentication(new CookieAuthenticationOptions() {
DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(#"C:\temp-keys\")),
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Login"),
AccessDeniedPath = new PathString("/Home/AccessDenied"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
With that configuration done. You can now decrypt the authentication cookie with the following code:
public IActionResult DecryptCookie() {
ViewData["Message"] = "This is the decrypt page";
var user = HttpContext.User; //User will be set to the ClaimsPrincipal
//Get the encrypted cookie value
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
var provider = DataProtectionProvider.Create(new DirectoryInfo(#"C:\temp-keys\"));
//Get a data protector to use with either approach
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get teh decrypted cookies as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue);
return View();
}
You can learn more about this latter scenario here: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing
While inside ASP.NET Core app you can just use CookieAuthenticationOptions.TicketDataFormat.Unprotect(cookieValue).
Here, a simple static (!) method I wrote:
public static AuthenticationTicket DecryptAuthCookie(HttpContext httpContext)
{
// ONE - grab the CookieAuthenticationOptions instance
var opt = httpContext.RequestServices
.GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>()
.Get(CookieAuthenticationDefaults.AuthenticationScheme); //or use .Get("Cookies")
// TWO - Get the encrypted cookie value
var cookie = opt.CookieManager.GetRequestCookie(httpContext, opt.Cookie.Name);
// THREE - decrypt it
return opt.TicketDataFormat.Unprotect(cookie);
}
Works fine under .NET 5 and .NET 6.
I'm adding this answer for reference, because this question pops up on every search engine if you search for how to manually decrypt ASP.NET auth cookie.
See below a helper method for .NET Core 2 to get claims from a cookie:
private IEnumerable<Claim> GetClaimFromCookie(HttpContext httpContext, string cookieName, string cookieSchema)
{
// Get the encrypted cookie value
var opt = httpContext.RequestServices.GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>();
var cookie = opt.CurrentValue.CookieManager.GetRequestCookie(httpContext, cookieName);
// Decrypt if found
if (!string.IsNullOrEmpty(cookie))
{
var dataProtector = opt.CurrentValue.DataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", cookieSchema, "v2");
var ticketDataFormat = new TicketDataFormat(dataProtector);
var ticket = ticketDataFormat.Unprotect(cookie);
return ticket.Principal.Claims;
}
return null;
}
As was pointed by #Cirem, the dodgy way of creating a protector is exactly how Microsoft does it (see their code here). Therefore, it may change in future versions.
Another variation for ASP.NET Core 2.2:
var cookieManager = new ChunkingCookieManager();
var cookie = cookieManager.GetRequestCookie(HttpContext, ".AspNetCore.Identity.Application");
var dataProtector = dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Identity.Application", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookie);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get teh decrypted cookies as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie);
I just got this working in Classic ASP.net (4.6.1). Note the following required installs:
Microsoft.Owin.Security.Interop (will come with a bunch of dependencies - note, I used verison 3.0.1 due to an exception, but that might not be necessary).
Microsfot.AspNetCore.DataProtection (will come with a bunch of dependencies)
Standard web stuff for the 4.6.1 framework
The following constants are defined by the framework:
PROVIDER_NAME = "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware"
SCHEME_NAME = "Identity.Application"
COOKIE_NAME = ".AspNetCore.Identity.Application" (can be customized)
The following constants are configuration-specific, but must be the same between applications.
APP_NAME = "Auth.Test.App"
SHARED_KEY_DIR = "C:\\app-keyring"
The Process:
This article was helpful in getting this set up on both sides, but particularly in properly configuring the .Net Core side. Thus, we shall leave that as an exercise for the reader.
Once you have these set up, on the 4.6.1 Decryption Side, the following code will yield the ClaimsIdentity set in (for example) .Net Core 3.0:
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Owin.Security.Interop;
using System.IO;
using System.Security.Claims;
using System.Web;
public static ClaimsIdentity GetClaimsIdentity(HttpContext context)
{
//Get the encrypted cookie value
var cookie = context.Request.Cookies[Constants.COOKIE_NAME];
if (cookie == null) {
return null;
}
var cookieValue = cookie.Value;
//Get a data protector to use with either approach
var keysDir = new DirectoryInfo(Constants.SHARED_KEY_DIR);
if (!keysDir.Exists) { keysDir.Create(); }
var provider = DataProtectionProvider.Create(keysDir,
options => options.SetApplicationName(Constants.APP_NAME));
var dataProtector = provider.CreateProtector(Constants.PROVIDER_NAME, Constants.SCHEME_NAME, "v2");
//Get the decrypted cookie as a Authentication Ticket
var shim = new DataProtectorShim(dataProtector);
var ticketDataFormat = new AspNetTicketDataFormat(shim);
var ticket = ticketDataFormat.Unprotect(cookieValue);
return ticket.Identity;
}

NullReferenceException in Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireToken

I'm trying to get some MSFT Power BI SDK samples working. Unfortunately, the Microsoft.IdentityModel.Clients.ActiveDirectory library is giving me a lot of trouble with the initial external authentication step.
I'm using Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.28.3.860, from NuGet; this is the last version of the library before AcquireToken was removed, and I haven't figured out how to use its replacement (AcquireTokenAsync) in a way that's equivalent to what I see in the samples.
When I take the following code and modify the TODO lines to specify my actual Azure Client ID and authentication redirect page, I get as far as the AcquireToken line.
using System;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
public class Application
{
public static void Main(string[] args)
{
try
{
string clientID = "abcdef01-1234-1234-abcd-abcdabcd1234"; // TODO: actual Azure client ID
string redirectUri = "https://acmecorporation.okta.com/login/do-login"; // TODO: actual redirect
string resourceUri = "https://analysis.windows.net/powerbi/api";
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
AuthenticationResult ar = authContext.AcquireToken(
resourceUri,
clientID,
new Uri(redirectUri),
PromptBehavior.RefreshSession);
string token = ar.AccessToken;
Console.WriteLine("Success: " + token);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
At this point:
A "Sign in to your account" window pops up with the name of the app I've associated in Azure with the clientID GUID
I'm redirected to my organization's ("acmecorporation") sign-on page
I sign in using my AD credentials
The AcquireToken method throws the following NullReferenceExpection:
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)
at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireToken(String resource, String clientId, Uri redirectUri, PromptBehavior promptBehavior)
at PowerBISample.Application.Main(String[] args) in \\noxfile\users\ehirst\documents\visual studio 2015\Projects\PowerBISample\PowerBISample\Program.cs:line 18
Can anyone provide guidance on how to get past this? My goal is to get a POC working to determine whether we can integrate Power BI into a larger application, but so far it feels like I'm beta testing a pretty unstable system.
The NullReferenceException is a bug in the 2.x version of the ADAL library; it's fixed in current versions. It was triggered by an incorrect value of redirectUri; unfortunately the documentation was sparse on this one. A working code sample, adapted (thanks Kanishk!) to use the current 3.13.7 version of ADAL, is posted below.
namespace PowerBISample
{
using System;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
public class Application
{
public static void Main(string[] args)
{
Run();
Console.ReadLine();
}
static async void Run()
{
try
{
string clientID = "abcdef01-1234-1234-abcd-abcdabcd1234"; // TODO: actual Azure client ID
/** THE REAL PROBLEM WAS HERE **/
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
string resourceUri = "https://analysis.windows.net/powerbi/api";
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
AuthenticationResult ar = await authContext.AcquireTokenAsync(resourceUri, clientID, new Uri(redirectUri), new PlatformParameters(PromptBehavior.RefreshSession));
string token = ar.AccessToken;
Console.WriteLine("Success: " + token);
}
catch (Exception ex)
{
string error = ex.ToString();
Console.WriteLine(error);
}
}
}
}

Getting Available Certificate uploaded on azure storage

I want to get all uploaded certificate on storage in azure to use these certificate associate with VM when I creating VM's using rest-api to.
Is it necessary, the certificate should available on local machine?
If yes, is there any way to install certificate locally, when the web site/ portal in open on any machine.
You need to install the certificate on each machine that uses REST api to be able to function.
The point of the private and public keys are to maintain security. I dont think this would be something you would want to put on a website for anyone to be able to install.
That being said, if you are making the REST call via a website, then only the server hosting the application needs to have the certificate installed.
I build a webrequest that has the REST URL in it, like this one, then build the response.
private HttpWebResponse CallAzure(HttpWebRequest request, string postData)
{
var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadOnly);
var certs = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false);
if (request.Method.ToUpper() == "POST")
{
var xDoc = new XmlDocument();
xDoc.LoadXml(postData);
var requestStream = request.GetRequestStream();
var streamWriter = new StreamWriter(requestStream, Encoding.UTF8);
xDoc.Save(streamWriter);
streamWriter.Close();
requestStream.Close();
}
request.ClientCertificates.Add(certs[0]);
request.ContentType = "application/xml";
request.Headers.Add("x-ms-version", "2012-03-01");
ServicePointManager.Expect100Continue = false;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
request.ServicePoint.Expect100Continue = false;
var response = request.GetResponse();
return (HttpWebResponse)response;
}
I have found it easiest to install the certificate via PowerShell.
If you want to generate your own publishsettingfile here is a very easy app to do it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace CreatePublishSettingsFile
{
class Program
{
private static string subscriptionId = "[your subscription id]";
private static string subscriptionName = "My Awesome Subscription";
private static string certificateThumbprint = "[certificate thumbprint. the certificate must have private key]";
private static StoreLocation certificateStoreLocation = StoreLocation.CurrentUser;
private static StoreName certificateStoreName = StoreName.My;
private static string publishFileFormat = #"<?xml version=""1.0"" encoding=""utf-8""?>
<PublishData>
<PublishProfile
PublishMethod=""AzureServiceManagementAPI""
Url=""https://management.core.windows.net/""
ManagementCertificate=""{0}"">
<Subscription
Id=""{1}""
Name=""{2}"" />
</PublishProfile>
</PublishData>";
static void Main(string[] args)
{
X509Store certificateStore = new X509Store(certificateStoreName, certificateStoreLocation);
certificateStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = certificateStore.Certificates;
var matchingCertificates = certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false);
if (matchingCertificates.Count == 0)
{
Console.WriteLine("No matching certificate found. Please ensure that proper values are specified for Certificate Store Name, Location and Thumbprint");
}
else
{
var certificate = matchingCertificates[0];
certificateData = Convert.ToBase64String(certificate.Export(X509ContentType.Pkcs12, string.Empty));
if (string.IsNullOrWhiteSpace(subscriptionName))
{
subscriptionName = subscriptionId;
}
string publishSettingsFileData = string.Format(publishFileFormat, certificateData, subscriptionId, subscriptionName);
string fileName = Path.GetTempPath() + subscriptionId + ".publishsettings";
File.WriteAllBytes(fileName, Encoding.UTF8.GetBytes(publishSettingsFileData));
Console.WriteLine("Publish settings file written successfully at: " + fileName);
}
Console.WriteLine("Press any key to terminate the program.");
Console.ReadLine();
}
}
}

Azure connection failed error

i am using service this https://xxxx.accesscontrol.windows.net/v2/mgmt/service url to get
ACS token,i am getting this error,
ACS60018: The URI
'https://xxx.accesscontrol.windows.net/v2/mgmt/service' is not valid
since it is not based on
'https://xxxx.accesscontrol.windows.net/v2/mgmt/service/'. Trace ID:
ed498472-6a04-4d51-a6ba-4786f0c67212. Timestamp: 2012-10-16 07:07:09Z
if i try with an application
class Program
{
public const string serviceIdentityUsernameForManagement = "ManagementClient";
public const string serviceIdentityPasswordForManagement = "xxxxxxxxxxx=";
public const string serviceNamespace = "xxxxx";
public const string acsHostName = "accesscontrol.windows.net";
public const string acsManagementServicesRelativeUrl = "v2/mgmt/service/";
static string cachedSwtToken;
static void Main(string[] args)
{
//
// Request a token from ACS
//
WebClient client = new WebClient();
client.BaseAddress = string.Format(CultureInfo.CurrentCulture,
"https://{0}.{1}",
serviceNamespace,
acsHostName);
NameValueCollection values = new NameValueCollection();
values.Add("grant_type", "client_credentials");
values.Add("client_id", serviceIdentityUsernameForManagement);
values.Add("client_secret", serviceIdentityPasswordForManagement);
values.Add("scope", client.BaseAddress + acsManagementServicesRelativeUrl);
byte[] responseBytes = client.UploadValues("/v2/OAuth2-13", "POST", values);
string response = Encoding.UTF8.GetString(responseBytes);
// Parse the JSON response and return the access token
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> decodedDictionary = serializer.DeserializeObject(response) as Dictionary<string, object>;
string returnToken= decodedDictionary["access_token"] as string;
}
}
i am getting this error
"A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond
157.56.160.192:443"}
team can please tell me how do i get the ACS token by using c# code,
Thanks in Advance,
Saravanan
This error:
ACS60018: The URI 'https://xxx.accesscontrol.windows.net/v2/mgmt/service' is not valid since it is not based on 'https://xxxx.accesscontrol.windows.net/v2/mgmt/service/'. Trace ID: ed498472-6a04-4d51-a6ba-4786f0c67212. Timestamp: 2012-10-16 07:07:09Z
Is caused by a missing trailing slash on your management URI (the OData spec is particular about this).

Resources