ASP.Net Core and IIS: Use web.config to authorize Users/AD Groups

As a very fast and dirty solution to the authorization problem for an internally used web application (developed using asp.net core 2.1), I could put the old web.config file to good use: to allow certain users, add the following section:

<system.webServer>   
    <security>
      <authorization>
        <remove users="*" roles="" verbs="" />
        <add accessType="Allow" users="domain-name\user1, domain-name\user2" />
      </authorization>
    </security>
  </system.webServer>

Similarly, to allow members of a certain AD group add the follwoing:

<system.webServer>   
    <security>
      <authorization>
        <remove users="*" roles="" verbs="" />
        <add accessType="Allow" roles="domain-name\group-name" />
      </authorization>
    </security>
  </system.webServer>

For more information, see here. Hope this helps someone!

From Local Video to Azure Media Services

I got the task of building a video streaming prototype based on the Azure Media Services the other day. All I had was a sample .mp4 video, and an azure Subscription for testing purposes, and of course the boundless ocean of knowledge, the internet. I gleaned information from Microsoft documentations, Stack Overflow, etc. and I am going to summarize my experience here, hoping it saves someone’s time in future:

Creating Azure Media Services on the Azure Portal is straightforward and fast. I used a dedicated resource group to better see what components are involved and how much they would cost during testing usage. The setup consists of three components:

Create a Service Principal for the Media Services and replace the contents of the appsettings.json of the sample UploadEncodeAndStreamFiles program (see the Microsoft Documentation, or get it directly from Github). To achieve this, I did the following in a Powershell:

az login

az account set --subscription ssssssss-ssss-ssss-ssss-ssssssssssss

az ams account sp create --account-name <MediaServiceName> --resource-group <YourResourceGroup>

In my case, the <MediaServiceName> is just “damedia”, the name I had chosen as I created the Media Services on the Azure Portal. The result of the last command is a json, which we can use as our appsettings.json directly:

{
  "AadClientId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  "AadEndpoint": "https://login.microsoftonline.com",
  "AadSecret": "cccccccc-cccc-cccc-cccc-cccccccccccc",
  "AadTenantId": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
  "AccountName": "damedia",
  "ArmAadAudience": "https://management.core.windows.net/",
  "ArmEndpoint": "https://management.azure.com/",
  "Region": "West Europe",
  "ResourceGroup": "<YourResourceGroup>",
  "SubscriptionId": "ssssssss-ssss-ssss-ssss-ssssssssssss"
}

Note: If you get the error

The subscription of 'ssssssss-ssss-ssss-ssss-ssssssssssss' doesn't exist in cloud 'AzureCloud'.

run:

az account clear
az login

and make sure you have chosen the correct account.

I just replaced the sample video ‘ignite.mp4’ with my video and started the program. It takes a while to complete, depending on the size and quality of the video, and generates the following logs in the console:

....
Job finished.
Downloading output results to 'Output\output-MySampleVideo-20200619-134744.mp4'...
Download complete.
-------------------------
locatorObject_name: locator-MySampleVideo-20200619-134744.mp4
-------------------------
-------------------------
streamingEndpointHostName: mediaservicename-euwe.streaming.media.azure.net
-------------------------
https://mediaservicename-euwe.streaming.media.azure.net/uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu/MySampleVideo.ism/manifest(format=m3u8-aapl)
https://mediaservicename-euwe.streaming.media.azure.net/uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu/MySampleVideo.ism/manifest(format=mpd-time-csf)
https://mediaservicename-euwe.streaming.media.azure.net/uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu/MySampleVideo.ism/manifest
Done. Copy and paste the Streaming URL into the Azure Media Player at 'http://aka.ms/azuremediaplayer'.
Press Enter to continue.

In the Azure storage, we see now two new containers:

One of them contains the original video, uploaded by the program. The other one contains various copies of the video, with different sizes, each of which accompanied with an .mpi file, which Microsoft says are “intended to improve performance for dynamic packaging and streaming“…

Now I am ready to create a simple html file and embed an Azure Media Player in it:

<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Azure Media Player</title>
    <meta name="description" content="Test...">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!--*****START OF Azure Media Player Scripts*****-->
        <!--Note: DO NOT USE the "latest" folder in production. Replace "latest" with a version number like "1.0.0"-->
        <!--EX:<script src="//amp.azure.net/libs/amp/1.0.0/azuremediaplayer.min.js"></script>-->
        <!--Azure Media Player versions can be queried from //aka.ms/ampchangelog-->
        <link href="https://amp.azure.net/libs/amp/2.3.5/skins/amp-default/azuremediaplayer.min.css" rel="stylesheet">
        <script src="https://amp.azure.net/libs/amp/2.3.5/azuremediaplayer.min.js"></script>
    
    <!--*****END OF Azure Media Player Scripts*****-->

</head>
<body>
    <h1>Sample: Introduction</h1>
    <h3>contact: john.doe@me.com</h3>

    <video id="azuremediaplayer" class="azuremediaplayer amp-default-skin amp-big-play-centered" tabindex="0"></video>
    <footer>
        <br />
        <p>© ME2020</p>
    </footer>

    <script>
        var myOptions = {
	"nativeControlsForTouch": false,
	controls: true,
	autoplay: true,
	width: "640",
	height: "400",
}
myPlayer = amp("azuremediaplayer", myOptions);
myPlayer.src([
        {
                "src": "https://mediaservicename-euwe.streaming.media.azure.net/uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu/MySampleVideo.ism/manifest",
                "type": "application/vnd.ms-sstr+xml"
        }
]);
    </script>
</body>
</html>

The “src” link above is just the third link we get after running the console program. Open this html file in a web browser, et voila!!

Note: Make sure to use the latest version of the scripts by checking the Azure Media Player Releases web site.

Azure Key Vault: Transfer secrets using Powershell (Different Subscriptions)

Recently I had to copy more than 50 secrets (names and values) from one Azure KeyVault to another one. The two KeyVaults are on different subscriptions. Doing this manually is very tiresome and error prone. So I decided to do it in the right way…

Here is my favorite reference for Azure Powershell modules and commands.

So back to the work, first of all I imported the Az.KeyVault module:

Import-Module Az.KeyVault

Then I needed to login and connect to the Azure subscription containing the source KeyVault:

Connect-AzAccount -SubscriptionId 'ssssssss-ssss-ssss-ssss-ssssssssssss'

Having done that, I proceeded with running the Get-AzKeyVaultSecret module and saving the secret names in a list:

$sourceVaultName = "skv"
$targetVaultName ="tkv"
$secretNames = (Get-AzKeyVaultSecret -VaultName $sourceVaultName).Name

Now I could loop through these names, use Get-AzKeyVaultSecret again, and get the secret values. Note that the “disabled” secrets have null value. So I did a simple “null-check” before saving the name-value pairs in the final list:

$secretValuePairs = @()

foreach ($secret in $secretNames)
{
 
    $obj = [PSCustomObject]@{
        Name = $secret
        Value = (Get-AzKeyVaultSecret -VaultName $sourceVaultName -Name $secret).SecretValue
    }
       
    if ($obj.Value -ne $null) {
        $secretValuePairs += $obj
        Write-Host "$($obj.Name) : $($obj.Value)"
    }

}

Now all I had to do was, to change the subscription and import the secret key-value pairs to the destination KeyVault:

Connect-AzAccount -SubscriptionId 'tttttttt-tttt-tttt-tttt-tttttttttttt'

$secretValuePairs.foreach{
    Set-AzKeyVaultSecret -VaultName $targetVaultName -Name $_.Name -SecretValue $_.Value
}

This way I managed to transfer the secrets fast and mistake-free. I hope this saves someone’s time in future.

Java: Key store and pfx files

There is a Java-based web application in our company which is going to be extended to be able to communicate with a certain tax authority through an API. The tax authority issues a pfx file containing a key and a certificate and expects all API requests to contain the pfx (as base64 string) and its pin (the password for encrypting the pfx) in certain fields. The Java-based application uses a jks Java key store to store all kind of certificates and keys. The team has decided that the pfx should be stored in the key sore using Java Key Tool. The service responsible for communication with the tax authority then has to retrieve it and use it for the communication through the API. This is exactly what I am supposed to implement. Let’s start then.

Beside the Road: pfx files

A PFX file, is a password protected PKCS #12  archive. It contains the entire certificate chain plus the matching private keys.

Beside the Road: Java Keytool

The Keytool executable is distributed with the Java SDK. Here is a nice tutorial about it. I just add two commands for convenience below.

  • How to use keytool command to see what is inside a pfx or key store file:
keytool -v -list -storetype pkcs12 -keystore ("path to the pfx file")
keytool -v -list -storetype jks -keystore ("path to the jks file")
  • How to use keytool command to add a pfx to a key store
keytool -importkeystore -srckeystore ("path to the pfx file") -srcstoretype pkcs12 -destkeystore ("path to the jks file") -deststoretype jks -deststorepass ("the password of the jks")

First off, I noticed the pfx has been decrypted and its contents are added to the key store. So I need to somehow recreate the pfx as base 64 string. I use the “KeyStore Explorer” on Ubuntu as a graphical tool to examine jks as well as pfx files:

Please note that this is just a sample jks file. the real key store contains a lot of other keys and certificates…

Notice the two tiny locks in the above screenshot: these indicate that the entries in the key store are also locked.

Getting the content from the key store is easy. You just need to know what you are looking for, or rather, its alias. (Use the KeyStore Explorer or keytool -v -list as above to find out what is inside with their corresponding aliases.) Then just load the key store:

KeyStore keyStore = KeyStore.getInstance("JKS");

keyStore.load(new FileInputStream("keystore.jks"), KEYPASS.toCharArray());

Here KEYPASS is the so called pin or password for decrypting the key store. Now we can retrieve the two fellows as follows:

java.security.cert.Certificate[] chain1 = keyStore.getCertificateChain("encryptionkey");

java.security.cert.Certificate[] chain2 = keyStore.getCertificateChain("signaturekey");

PrivateKey key1 = (PrivateKey)keyStore.getKey("encryptionkey", KEYPASS_ORG_PFX.toCharArray());

PrivateKey key2 = (PrivateKey)keyStore.getKey("signaturekey", KEYPASS_ORG_PFX.toCharArray());

Notice how I am reading each alias once as a certificate chain and once as a key. To recreate the pfx file, I need them both. Notice also, to retrieve the key1 and key2 I have to know “KEYPASS_ORG_PFX”, the corresponding protection key from the original pfx.

What I am going to do next is, to create a protection parameter and two entries as the ingredients of my pfx file as follows:

KeyStore.ProtectionParameter protParam =
        new KeyStore.PasswordProtection(KEYPASS_NEW_PFX.toCharArray());

KeyStore.PrivateKeyEntry pkEntry1 =
        new KeyStore.PrivateKeyEntry(key1, chain1);

KeyStore.PrivateKeyEntry pkEntry2 =
        new KeyStore.PrivateKeyEntry(key2, chain2);

The KEYPASS_NEW_PFX is the key encrypting the entries of the newly created pfx file, which we can choose here (it has nothing to do with the key store password or the KEYPASS_ORG_PFX). I have to encrypt the entries, it is a requirement for the pfx to be accepted by the tax authorities. Having the ingredients, I just need to create a new Keystore object and add them to it:

KeyStore keyStorePfx = KeyStore.getInstance("PKCS12");

keyStorePfx.load(null, null);

keyStorePfx.setEntry("encryptionkey", pkEntry1, protParam);

keyStorePfx.setEntry("signaturekey", pkEntry2, protParam);

Finally, I need to write it to an OutputStream and convert it to base64 string:

ByteArrayOutputStream bs = new ByteArrayOutputStream();

keyStorePfx.store(bs, KEYPASS_NEW_PFX.toCharArray());

byte[] pfxBytes = Base64.getEncoder().encode(bs.toByteArray());

String pfxBase64 = new String(encoded);

I just used the same KEYPASS_NEW_PFX for encrypting the pfx. The resulting base64 string is written to the pfxBase64, ready to be used.