Skip to content

ASP.NET with windows-authentication and authorization via web.config

Recently I experienced problems with getting windows-authentication and authorization to work with web.config. So here is a reminder.

This did not work, gave a logon-prompt:

<configuration>
    <system.web>
        <authentication mode="Windows" />
        <authorization>
             <deny users="*" />
             <allow roles="The-Domain-Group-You-Are-Member-Of" />
        </authorization>
    </system.web>
</configuration>

I had to switch order of the tags, like this:

<configuration>
    <system.web>
        <authentication mode="Windows" />
        <authorization>
             <allow roles="The-Domain-Group-You-Are-Member-Of" />
             <deny users="*" />
        </authorization>
    </system.web>
</configuration>
Advertisements

Selfsigned wildcard certificate in Windows

If you want to test/laborate and need a wildcard certificate (SSL-certificate) you can create a selfsigned wildcard certificate in Windows.

On the web-server (IIS):

New-SelfSignedCertificate -CertStoreLocation "Cert:\LocalMachine\My" -DnsName "*.yourdomain.net" -Subject "Whatever-Name-You-Like";

Now you can use the certificate for the https-binding. Export the certificate and import it on the client to the “Cert:\LocalMachine\Root”-store. The certificate is now cosidered secure in Chrome, Edge and IE.

Choose authentication-type for WS-Federation in ADFS

If you want to choose the authentication-type when you configure an application for ADFS/WS-Federation using Windows Identity Foundation you can do it in the wsFederation-element:

<system.identityModel.services>
    <federationConfiguration>
        <wsFederation
            authenticationType=""
            ...
        />
    </federationConfiguration>
</system.identityModel.services>

To know what values you can enter go to the ADFS-server and run the following PowerShell-command:

Get-AdfsAuthenticationProvider;

Look at the AuthenticationMethods-attribute in the result.

Forms Authentication

Windows Authentication

Certificate Authentication

  • urn:ietf:rfc:2246
  • urn:oasis:names:tc:SAML:1.0:am:X509-PKI
  • urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient
  • urn:oasis:names:tc:SAML:2.0:ac:classes:X509

Device Authentication

Azure MFA (AzurePrimaryAuthentication)

Azure MFA (AzureMfaAuthentication)

Microsoft Passport Authentication

  • urn:ietf:rfc:2246
  • urn:oasis:names:tc:SAML:1.0:am:X509-PKI
  • urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient
  • urn:oasis:names:tc:SAML:2.0:ac:classes:X509

Help/information

Scheduled backup jobs – SQL Server

This is a reminder for myself to setup scheduled backup jobs in SQL Server. The scheduled-job-script is for a SQL Server 2017 installation and for an installation on a D-partition. If you are using another version replace “MSSQL14” with “MSSQLXX” (where XX is the version you are using) in the scheduled-job-script. If you are using a SQL Server installation on the C-partition replace “D:\” with “C:\” in the scheduled-job-script. There are also some cleanup and optimization jobs. The jobs requires some custom stored procedures and functions.

How can I automate certificate-import to group-policy

I am building ARM-templates to set up test-environments in Azure. I am using DSC to set up the different machines. One thing I want to automate is to import a certificate to the group-policies. You can do it like this manually on the domain-controller (Active-Directory server):

  • Group Policy Management -> Forest: mydomain.net -> Domains -> mydomain.net -> Group Policy Objects -> Default Domain Policy
  • Right click -> Edit
  • Default Domain Policy -> Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Public Key Policies -> Trusted Root Certification Authorities
  • Right click -> Import

I have laborated with Import-PfxCertificate, CertUtil.exe and .NET C# to accomplish it but haven’t succeeded. What I have tested you can see below, I have put some comments about my thoughts.

Can anyone help me? How should I do this?

First we create a certificate and export it and finally we delete it (we keep the exported one):

$certificateStoreLocation = "CERT:\LocalMachine\My";
$password = ConvertTo-SecureString -String "P@ssword12" -Force -AsPlainText;
 
$certificate = New-SelfSignedCertificate -CertStoreLocation $certificateStoreLocation -DnsName "Test-Certificate";
 
$certificateLocation = "$($certificateStoreLocation)\$($certificate.Thumbprint)";
 
$result = Export-PfxCertificate -Cert $certificateLocation -FilePath "C:\Data\Certificates\Test-Certificate.pfx" -Password $password;
 
Get-ChildItem $certificateLocation | Remove-Item;

List the certificate stores

foreach($item in Get-ChildItem "CERT:\")
{
	Write-Host " - CERT:\$($item.Location)\";
 
	foreach($store in $item.StoreNames.GetEnumerator())
	{
		Write-Host "   - CERT:\$($item.Location)\$($store.Name)";
	}
}

PowerShell – Import-PfxCertificate

$certificateStoreLocation = "CERT:\LocalMachine\Root";
$password = ConvertTo-SecureString -String "P@ssword12" -Force -AsPlainText;
 
Import-PfxCertificate -CertStoreLocation $certificateStoreLocation -FilePath "C:\Data\Certificates\Test-Certificate.pfx" -Password $password;
 
Get-ChildItem $certificateStoreLocation;
 
# Now you can find the certificate in the MMC Certificate Snapin:
# [Console Root\Certificates (Local Computer)\Trusted Root Certification Authorities\Certificates]
 
# Now you can find the certificate in the registry.
# Get-ChildItem "REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\";
 
# I want to put the certificate here:
# Get-ChildItem "REGISTRY::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\";

PowerShell – CertUtil

CertUtil -p "P@ssword12" -ImportPfx "Root" "C:\Data\Certificates\Test-Certificate.pfx";
 
Get-ChildItem "CERT:\LocalMachine\Root";
 
CertUtil -p "P@ssword12" -ImportPfx -GroupPolicy "Root" "C:\Data\Certificates\Test-Certificate.pfx"# No error but the same result as CertUtil -p "P@ssword12" -ImportPfx "Root" "C:\Data\Certificates\Test-Certificate.pfx".

.NET C#

using(var certificate = new X509Certificate2(@"C:\Data\Certificates\Test-Certificate.pfx""P@ssword12"))
{
	// We only have StoreLocation.CurrentUser and StoreLocation.LocalMachine.
	// Can I use System.Management.Automation.Security.NativeMethods+CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
	// somehow to create/open a store by calling new X509Store(IntPtr storeHandle).
 
	using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
	{
		store.Open(OpenFlags.ReadWrite);
		store.Add(certificate);
	}
}

My imaginary solution

Thought this was a possible solution:

  1. Import the certificate to “CERT:\LocalMachine\Root”
  2. Move the registry-key “HKLM:\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\THUMBPRINT” to “HKLM:\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\THUMBPRINT”
  3. Restart the machine

The registry-keys get correct but the localmachine-root-certificate is still in the certificate-mmc-snapin and no root-certificate is found in the Group Policy Management console.

$certificateRegistryKeyPathPrefix = "HKLM:\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\";
$certificateStoreLocation = "CERT:\LocalMachine\Root";
$password = ConvertTo-SecureString -String "P@ssword12" -Force -AsPlainText;
$pfxCertificatePath = "C:\Data\Certificates\Test-Certificate.pfx";
$policyCertificateRegistryKeyPathPrefix = "HKLM:\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\";
 
# Get the thumbprint from the pfx-file so we can check if it's already in the registry.
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2;
$certificate.Import($pfxCertificatePath, $password, "DefaultKeySet");
 
$policyCertificateRegistryKeyPath = "$($policyCertificateRegistryKeyPathPrefix)$($certificate.Thumbprint)";
 
$policyCertificateRegistryKey = Get-Item -ErrorAction SilentlyContinue -Path $policyCertificateRegistryKeyPath;
 
if(!$policyCertificateRegistryKey)
{
	$certificateRegistryKeyPath = "$($certificateRegistryKeyPathPrefix)$($certificate.Thumbprint)";
 
	$certificateRegistryKey = Get-Item -ErrorAction SilentlyContinue -Path $certificateRegistryKeyPath;
 
	if(!$certificateRegistryKey)
	{
		$certificate = Import-PfxCertificate -CertStoreLocation $certificateStoreLocation -FilePath $pfxCertificatePath -Password $password;
 
		$certificateRegistryKey = Get-Item -Path $certificateRegistryKeyPath;
	}
 
	Move-Item -Destination $policyCertificateRegistryKeyPath -Path $certificateRegistryKeyPath;
 
	# And then we need to reboot the machine.
}

Building a Sybase-NuGet-package with NuGet Package Explorer

This package will handle the unmanaged Sybase-assemblies that can not be referenced in your project but have to reside in the “bin”-folder. I will call the package Company.Sybase.1.0.0.nupkg.

Package structure

  • build
    • net
      • Company.Sybase.targets
  • lib
    • net20
      • Sybase.AdoNet2.AseClient.dll
      • Sybase.Data.AseClient.dll
    • net40
      • Sybase.AdoNet4.AseClient.dll
      • Sybase.Data.AseClient.dll
    • Unmanaged
      • sybdrvado115a.dll

Company.Sybase.nuspec

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
	<metadata>
		<authors>Company</authors>
		<description>Sybase assemblies.</description>
		<id>Company.Sybase</id>
		<owners>Company</owners>
		<references>
			<reference file="Sybase.AdoNet2.AseClient.dll" />
			<reference file="Sybase.AdoNet4.AseClient.dll" />
			<reference file="Sybase.Data.AseClient.dll" />
		</references>
		<requireLicenseAcceptance>false</requireLicenseAcceptance>
		<title>Company.Sybase</title>
		<version>1.0.0</version>
	</metadata>
</package>

Company.Sybase.targets

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<PropertyGroup>
		<BuildDependsOn>
			ResolveSybaseReferenceDependencies;
			ResolveSybaseDependenciesOnBuild;
			$(BuildDependsOn);
		</BuildDependsOn>
		<CleanDependsOn>
			ResolveOutputPath;
			ResolveSybaseReferenceDependencies;
			ResolveSybaseDependenciesOnClean;
			$(CleanDependsOn);
		</CleanDependsOn>
		<CopyAllFilesToSingleFolderForPackageDependsOn>
			ResolveOutputPath;
			ResolveSybaseReferenceDependencies;
			ResolveSybaseDependenciesOnPublish;
			$(CopyAllFilesToSingleFolderForPackageDependsOn);
		</CopyAllFilesToSingleFolderForPackageDependsOn>
	</PropertyGroup>
	<Target Name="ResolveOutputPath">
		<PropertyGroup>
			<_ResolvedOutputPath>$(OutputPath)</_ResolvedOutputPath>
			<_ResolvedOutputPath Condition="!$(_ResolvedOutputPath.EndsWith('\'))">$(_ResolvedOutputPath)\</_ResolvedOutputPath>
		</PropertyGroup>
	</Target>
	<Target Name="ResolveSybaseDependenciesOnBuild">
		<Copy
			DestinationFolder="$(OutputPath)"
			SourceFiles="@(_SybaseReferenceDependency)"
		/>
	</Target>
	<Target Name="ResolveSybaseDependenciesOnClean">
		<Delete
			Files="$(_ResolvedOutputPath)%(_SybaseReference.Filename)%(_SybaseReference.Extension)"
		/>
		<Delete
			Files="$(_ResolvedOutputPath)%(_SybaseReferenceDependency.Filename)%(_SybaseReferenceDependency.Extension)"
		/>
	</Target>
	<Target Name="ResolveSybaseDependenciesOnPublish">
		<ItemGroup>
			<FilesForPackagingFromProject Include="@(_SybaseReferenceDependency)">
				<DestinationRelativePath>$(_ResolvedOutputPath)%(Filename)%(Extension)</DestinationRelativePath>
			</FilesForPackagingFromProject>
		</ItemGroup>
	</Target>
	<Target Name="ResolveSybaseReferenceDependencies">
		<ItemGroup>
			<_SybaseDataAseClientReferenceInternal Include="@(Reference)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('Sybase.Data.AseClient'))" />
		</ItemGroup>
		<PropertyGroup>
			<_SybaseDataAseClientReferenceInternalHintPath>%(_SybaseDataAseClientReferenceInternal.HintPath)</_SybaseDataAseClientReferenceInternalHintPath>
		</PropertyGroup>
		<PropertyGroup>
			<_SybasePackageLibraryPath>$(_SybaseDataAseClientReferenceInternalHintPath.Substring(0, $([MSBuild]::Add($(_SybaseDataAseClientReferenceInternalHintPath.IndexOf('\lib\')), 5))))</_SybasePackageLibraryPath>
		</PropertyGroup>
		<ItemGroup>
			<_SybaseReference Include="%(Reference.HintPath)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('Sybase.'))" />
		</ItemGroup>
		<ItemGroup>
			<_SybaseReferenceDependency Include="$(_SybasePackageLibraryPath)Unmanaged\*.dll" />
		</ItemGroup>
	</Target>
</Project>

Trigger a reboot with DSC Script Resource

Script RebootWhenSet
{
	GetScript = {
		# TestScript = $true
		return @{ Result = "Some value" };
 
        	# TestScript = $false
		# return @{ Result = $null };
	}
	SetScript = {
        	# Set things
        	# ...
 
		$global:DSCMachineStatus = 1;
	}
	TestScript = {
		$result = (Invoke-Expression -Command $GetScript)["Result"];
 
		if($result)
		{
			return $true;
		}
 
		return $false;
	}
}