Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed certificates/serviceprovider ssl.pfx
Binary file not shown.
Binary file removed certificates/serviceprovider-expired.pfx
Binary file not shown.
Binary file modified certificates/serviceprovider.p12
Binary file not shown.
117 changes: 104 additions & 13 deletions setup/functions.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,120 @@ function Set-CertificatePermission

$cert = Get-ChildItem -Path cert:\LocalMachine\My | Where-Object -FilterScript { $PSItem.ThumbPrint -eq $pfxThumbPrint; };

if ($null -eq $cert)
{
throw "Certificate with thumbprint $pfxThumbPrint was not found in Cert:\LocalMachine\My";
}

# Specify the user, the permissions and the permission type
$permission = "$($serviceAccount)","Read,FullControl","Allow"
$accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;

# Location of the machine related keys
$keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\";
$keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
$keyFullPath = $keyPath + $keyName;
# Resolve the private key file in a provider-agnostic way so this works for both modern CNG keys
# and legacy CryptoAPI (CSP) keys. Depending on how the certificate was imported, the key may be
# stored under CNG (Microsoft\Crypto\Keys) or legacy CSP (Microsoft\Crypto\RSA\MachineKeys).
# The old code used $cert.PrivateKey.CspKeyContainerInfo directly, which throws for CNG-stored keys.
# Collect every candidate location and apply the ACL to whichever file actually exists.
$candidateFiles = New-Object System.Collections.Generic.List[string];

# CNG keys: Microsoft\Crypto\Keys, container file name = CngKey.UniqueName
try
{
# Get the current acl of the private key
# This is the line that fails!
$acl = Get-Acl -Path $keyFullPath;
$cngRsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert);
if ($cngRsa -is [System.Security.Cryptography.RSACng] -and -not [string]::IsNullOrEmpty($cngRsa.Key.UniqueName))
{
$candidateFiles.Add($env:ProgramData + "\Microsoft\Crypto\Keys\" + $cngRsa.Key.UniqueName);
}
}
catch { }

# Add the new ace to the acl of the private key
$acl.AddAccessRule($accessRule);
# Legacy CSP keys: Microsoft\Crypto\RSA\MachineKeys, container file name = UniqueKeyContainerName
try
{
$cspRsa = $cert.PrivateKey;
if ($null -ne $cspRsa -and $null -ne $cspRsa.CspKeyContainerInfo -and -not [string]::IsNullOrEmpty($cspRsa.CspKeyContainerInfo.UniqueKeyContainerName))
{
$candidateFiles.Add($env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\" + $cspRsa.CspKeyContainerInfo.UniqueKeyContainerName);
}
}
catch { }

# Write back the new acl
Set-Acl -Path $keyFullPath -AclObject $acl;
$applied = $false;
foreach ($keyFullPath in ($candidateFiles | Select-Object -Unique))
{
if (Test-Path -Path $keyFullPath)
{
$acl = Get-Acl -Path $keyFullPath;
$acl.AddAccessRule($accessRule);
Set-Acl -Path $keyFullPath -AclObject $acl;
$applied = $true;
}
}

if (-not $applied)
{
throw "Could not locate the private key file for certificate $pfxThumbPrint (checked both the CNG and CSP key stores). The certificate's key provider metadata may be inconsistent - re-import the certificate cleanly.";
}
}

function Set-CertificateChainTrust
{
# Ensures the CA chain contained in a PFX is trusted for X.509 chain building: the root CA is placed
# in Trusted Root (LocalMachine\Root) and intermediate CAs in Intermediate CA (LocalMachine\CA).
# Import-PfxCertificate drops all CA certificates - including the root - into the Intermediate CA store,
# where a root is not a trust anchor, so any misplaced root copy is removed from Intermediate CA.
# Without this, DefaultCertificateSpecification (chain + online revocation) rejects the certificate
# because the chain terminates in an untrusted root (see GitHub issue #70).
param
(
[Parameter(Position=1, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$pfxPath,

[Parameter(Position=2, Mandatory=$true)]
[ValidateNotNull()]
[System.Security.SecureString]$pfxPassword
)

$plainPassword = (New-Object System.Net.NetworkCredential('', $pfxPassword)).Password

# X509Certificate2Collection.Import is a raw .NET call and resolves relative paths against the process
# working directory, not PowerShell's current location (Set-Location / $PSScriptRoot). Resolve to an
# absolute path first so a relative $pfxPath works the same way as the Import-PfxCertificate calls do.
$resolvedPfxPath = (Resolve-Path -Path $pfxPath).ProviderPath

$chain = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$chain.Import($resolvedPfxPath, $plainPassword, 'DefaultKeySet')

$intermediateStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('CA', 'LocalMachine')
$rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root', 'LocalMachine')
$intermediateStore.Open('ReadWrite')
$rootStore.Open('ReadWrite')
try
{
foreach ($cert in $chain)
{
if ($cert.HasPrivateKey) { continue } # leaf (end-entity) is imported separately into My

if ($cert.Subject -eq $cert.Issuer)
{
# Self-signed => root CA. Trust it, then remove any misplaced copy from Intermediate CA.
$rootStore.Add($cert);
$misplaced = $intermediateStore.Certificates.Find('FindByThumbprint', $cert.Thumbprint, $false);
foreach ($m in $misplaced) { $intermediateStore.Remove($m) }
write-host "Trusted root CA '$($cert.Subject)' ($($cert.Thumbprint)) in Trusted Root Certification Authorities"
}
else
{
# Intermediate CA
$intermediateStore.Add($cert);
write-host "Installed intermediate CA '$($cert.Subject)' ($($cert.Thumbprint)) in Intermediate Certification Authorities"
}
}
}
catch
finally
{
throw $_;
$intermediateStore.Close()
$rootStore.Close()
}
}
61 changes: 37 additions & 24 deletions setup/setup_prerequisites.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,63 @@ $certpassword = ConvertTo-SecureString -String "test1234" -AsPlainText -Force
$certpassword2 = ConvertTo-SecureString -String "Test1234" -AsPlainText -Force

write-host "Installing demoidp ssl certificate"
$demoIdpSslcertificate = Import-PfxCertificate '..\certificates\demoidp ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My
$demoIdpSslcertificate = Import-PfxCertificate '..\certificates\demoidp ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople
write-host "Installed demo idp ssl certificate $($sslcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"

write-host "Installing demoidp ssl certificate"
$serviceProviderSslcertificate = Import-PfxCertificate '..\certificates\serviceprovider ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My
$serviceProviderSslcertificate = Import-PfxCertificate '..\certificates\serviceprovider ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople
write-host "Installed demo idp ssl certificate $($sslcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"
$demoIdpSslcertificate = Import-PfxCertificate '..\certificates\demoidp ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My -Exportable
$demoIdpSslcertificate = Import-PfxCertificate '..\certificates\demoidp ssl.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople -Exportable
write-host "Installed demo idp ssl certificate $($demoIdpSslcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"

write-host "Installing demoidp's signing certificate"
$demoidpcertificate = Import-PfxCertificate '..\certificates\demoidp.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My
$demoidpcertificate = Import-PfxCertificate '..\certificates\demoidp.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople
$demoidpcertificate = Import-PfxCertificate '..\certificates\demoidp.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My -Exportable
$demoidpcertificate = Import-PfxCertificate '..\certificates\demoidp.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople -Exportable
write-host "Installed demoidp's signing certificate $($demoidpcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"
write-host "This certificate must be configured as the signing certificate for the demoidp"

write-host "Installing demoidp's expired signing certificate"
$demoidpexpiredcertificate = Import-PfxCertificate '..\certificates\demoidp-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My
$demoidpexpiredcertificate = Import-PfxCertificate '..\certificates\demoidp-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople
$demoidpexpiredcertificate = Import-PfxCertificate '..\certificates\demoidp-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My -Exportable
$demoidpexpiredcertificate = Import-PfxCertificate '..\certificates\demoidp-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople -Exportable
write-host "Installed demoidp's expired signing certificate $($demoidpexpiredcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"
write-host "This certificate is automatically configured as one of the signing certificates for the demoidp"

write-host "Installing serviceprovider's signing certificate"
$serviceprovidercertificate = Import-PfxCertificate '..\certificates\serviceprovider.p12' -Password $certpassword2 -CertStoreLocation Cert:\LocalMachine\My
$serviceprovidercertificate = Import-PfxCertificate '..\certificates\serviceprovider.p12' -Password $certpassword2 -CertStoreLocation Cert:\LocalMachine\TrustedPeople
$serviceprovidercertificate = Import-PfxCertificate '..\certificates\serviceprovider.p12' -Password $certpassword2 -CertStoreLocation Cert:\LocalMachine\My -Exportable
$serviceprovidercertificate = Import-PfxCertificate '..\certificates\serviceprovider.p12' -Password $certpassword2 -CertStoreLocation Cert:\LocalMachine\TrustedPeople -Exportable
write-host "Installed serviceprovider's signing certificate $($serviceprovidercertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"
write-host "This certificate is used by the demo website (service provider) as its current signing certificate"

write-host "Installing serviceprovider's expired signing certificate"
$serviceproviderexpiredcertificate = Import-PfxCertificate '..\certificates\serviceprovider-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\My
$serviceproviderexpiredcertificate = Import-PfxCertificate '..\certificates\serviceprovider-expired.pfx' -Password $certpassword -CertStoreLocation Cert:\LocalMachine\TrustedPeople
write-host "Installed serviceprovider's expired signing certificate $($serviceproviderexpiredcertificate.Thumbprint) in LocalMachine\My and LocalMachine\TrustedPeople. This ensures the certificate is trusted on your machine and browser"
write-host "This certificate is used by the demo website (service provider) as one of its signing certificates"
write-host "Trusting the serviceprovider certificate's CA chain so X.509 chain building succeeds (see GitHub issue #70)"
Set-CertificateChainTrust '..\certificates\serviceprovider.p12' $certpassword2

#If you need to redo sslcert binding, the following statements will delete previously creates ones
#"http delete sslcert ipport=0.0.0.0:20001" | netsh
#"http delete sslcert ipport=0.0.0.0:20002" | netsh

write-host "Registering demo idp ssl certificate $($demoIdpSslcertificate.Thumbprint) for SSL bindings for demo sites"
"http add sslcert ipport=0.0.0.0:20001 certhash=$($demoIdpSslcertificate.Thumbprint) appid={$([Guid]::NewGuid().ToString().ToUpper())}" | netsh
write-host "Registering service provider ssl certificate $($serviceProviderSslcertificate.Thumbprint) for SSL bindings for demo sites"
"http add sslcert ipport=0.0.0.0:20002 certhash=$($serviceProviderSslcertificate.Thumbprint) appid={$([Guid]::NewGuid().ToString().ToUpper())}" | netsh
# The service provider demo runs on https://localhost:20002. Bind the machine's localhost developer
# certificate (e.g. the IIS Express Development Certificate) to that port instead of a project-specific
# SSL certificate - it matches 'localhost' (no name-mismatch warning) and is already trusted.
$localhostDevCert = Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.FriendlyName -eq 'IIS Express Development Certificate' -or $_.Subject -eq 'CN=localhost' } |
Sort-Object NotAfter -Descending | Select-Object -First 1
if ($null -eq $localhostDevCert)
{
write-host "No localhost developer certificate found in Cert:\LocalMachine\My - creating a self-signed one"
$localhostDevCert = New-SelfSignedCertificate `
-Type SSLServerAuthentication `
-DnsName "localhost" `
-FriendlyName "OIOSAML demo localhost developer certificate" `
-CertStoreLocation "Cert:\LocalMachine\My" `
-NotAfter (Get-Date).AddYears(5) `
-KeyExportPolicy Exportable

# A self-signed certificate is its own root, so add it to Trusted Root so browsers trust https://localhost.
$rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine")
$rootStore.Open("ReadWrite")
$rootStore.Add($localhostDevCert)
$rootStore.Close()
write-host "Created and trusted self-signed localhost certificate $($localhostDevCert.Thumbprint)"
}
write-host "Registering localhost developer certificate $($localhostDevCert.Thumbprint) for the SSL binding on port 20002"
"http add sslcert ipport=0.0.0.0:20002 certhash=$($localhostDevCert.Thumbprint) appid={$([Guid]::NewGuid().ToString().ToUpper())}" | netsh

write-host "If you want to re-run the sslcert bindings, you must manually delete the sslcert's (look inside this script for guidance)"

Expand All @@ -62,11 +78,8 @@ write-host "Setting private key access for your identity $username on the servic
Set-CertificatePermission $serviceprovidercertificate.Thumbprint $username
write-host "Setting private key access for your identity $username on the demo idp signing expired certificate $($demoidpexpiredcertificate.Thumbprint) in the certificate store"
Set-CertificatePermission $demoidpexpiredcertificate.Thumbprint $username
write-host "Setting private key access for your identity $username on the service provider signing expired certificate $($serviceproviderexpiredcertificate.Thumbprint) in the certificate store"
Set-CertificatePermission $serviceproviderexpiredcertificate.Thumbprint $username

add-HostEntry "127.0.0.1" "oiosaml-demoidp.dk"
add-HostEntry "127.0.0.1" "oiosaml-net.dk"

write-host "Setup completed!"
write-host "You should now open the solution in Visual Studio, build it and run it!"
Expand Down
17 changes: 7 additions & 10 deletions src/dk.nita.saml20/WebsiteDemo/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
<AllowedClockSkewMinutes>3</AllowedClockSkewMinutes>
<PreventOpenRedirectAttack>true</PreventOpenRedirectAttack>
<SigningCertificates>
<SigningCertificate findValue="a402bb172929ae0d0ada62f6864329c35dc29483" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
<!--<SigningCertificate findValue="2FEF0ADA415E2FCC6E019E521C611CFF09F351F9" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>-->
<SigningCertificate findValue="894e87d4c9aabe6ae47d8af9f846734ba75a4e5e" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
</SigningCertificates>
<MetaDataShaHashingAlgorithm>SHA256</MetaDataShaHashingAlgorithm>
<AllowedAudienceUris>
Expand All @@ -65,18 +64,11 @@
<AppSwitchReturnURL platform="Android">sp0.test-nemlog-in.dk</AppSwitchReturnURL>
<AppSwitchReturnURL platform="iOS">sp1.test-nemlog-in.dk</AppSwitchReturnURL>
<ShowError>true</ShowError>
<ServiceProvider id="https://saml.oiosaml3-net.dk" server="https://oiosaml-net.dk:20002">
<ServiceProvider id="https://saml.oiosaml3-net.dk" server="https://localhost:20002">
<ServiceEndpoint localpath="/login.ashx" type="signon" redirectUrl="/MyPage.aspx?action=sso" index="0"/>
<ServiceEndpoint localpath="/logout.ashx" type="logout" redirectUrl="/Default.aspx" index="1"/>
<ServiceEndpoint localpath="/logout.ashx" type="soaplogout" errorBehaviour="throwexception"/>
<ServiceEndpoint localpath="/metadata.ashx" type="metadata"/>
<md:ContactPerson contactType="technical" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
<md:Company>Nets DanID A/S</md:Company>
<md:GivenName>Morten</md:GivenName>
<md:SurName>Bech</md:SurName>
<md:EmailAddress>mdbec@nets.eu</md:EmailAddress>
<md:TelephoneNumber>4</md:TelephoneNumber>
</md:ContactPerson>
</ServiceProvider>
<RequestedAttributes>
<!-- The attributes that the demonstration identity provider issues. -->
Expand Down Expand Up @@ -109,6 +101,11 @@
<add type="dk.nita.saml20.Specification.DefaultCertificateSpecification, dk.nita.saml20"/>
</CertificateValidation>
</add>
<add id="https://saml.test-nemlog-in.dk/" ShaHashingAlgorithm="SHA256">
<CertificateValidation>
<add type="dk.nita.saml20.Specification.DefaultCertificateSpecification, dk.nita.saml20"/>
</CertificateValidation>
</add>
<add id="https://oiosaml-demoidp.dk:20001/" ShaHashingAlgorithm="SHA512"/>
</IDPEndPoints>
<CommonDomain enabled="false" localReaderEndpoint="https://pfs04/demo/cdcreader.ashx"/>
Expand Down
3 changes: 2 additions & 1 deletion src/dk.nita.saml20/WebsiteDemo/WebsiteDemo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@
</ItemGroup>
<ItemGroup>
<Content Include="Default.aspx" />
<Content Include="idp-metadata\test-devtest4-idp-metadata.xml" />
<Content Include="idp-metadata\oiosaml3-idp-devtest4-inttest-25-11-26.xml" />
<Content Include="idp-metadata\oiosaml3-idp-inttest-25-11-28.xml" />
<Content Include="idp-metadata\local_demo_idp.xml" />
<Content Include="Web.config">
<SubType>Designer</SubType>
Expand Down
Loading
Loading