diff --git a/certificates/serviceprovider ssl.pfx b/certificates/serviceprovider ssl.pfx
deleted file mode 100644
index 0b08722..0000000
Binary files a/certificates/serviceprovider ssl.pfx and /dev/null differ
diff --git a/certificates/serviceprovider-expired.pfx b/certificates/serviceprovider-expired.pfx
deleted file mode 100644
index 08282b8..0000000
Binary files a/certificates/serviceprovider-expired.pfx and /dev/null differ
diff --git a/certificates/serviceprovider.p12 b/certificates/serviceprovider.p12
index 785b1a6..38e512e 100644
Binary files a/certificates/serviceprovider.p12 and b/certificates/serviceprovider.p12 differ
diff --git a/setup/functions.ps1 b/setup/functions.ps1
index ccb3459..b44a62a 100644
--- a/setup/functions.ps1
+++ b/setup/functions.ps1
@@ -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()
}
}
\ No newline at end of file
diff --git a/setup/setup_prerequisites.ps1 b/setup/setup_prerequisites.ps1
index 85b8a34..15ea6ee 100644
--- a/setup/setup_prerequisites.ps1
+++ b/setup/setup_prerequisites.ps1
@@ -11,38 +11,30 @@ $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
@@ -50,8 +42,32 @@ write-host "This certificate is used by the demo website (service provider) as o
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)"
@@ -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!"
diff --git a/src/dk.nita.saml20/WebsiteDemo/Web.config b/src/dk.nita.saml20/WebsiteDemo/Web.config
index 7f13ac2..aafdf49 100644
--- a/src/dk.nita.saml20/WebsiteDemo/Web.config
+++ b/src/dk.nita.saml20/WebsiteDemo/Web.config
@@ -44,8 +44,7 @@
3
true
-
-
+
SHA256
@@ -65,18 +64,11 @@
sp0.test-nemlog-in.dk
sp1.test-nemlog-in.dk
true
-
+
-
- Nets DanID A/S
- Morten
- Bech
- mdbec@nets.eu
- 4
-
@@ -109,6 +101,11 @@
+
+
+
+
+
diff --git a/src/dk.nita.saml20/WebsiteDemo/WebsiteDemo.csproj b/src/dk.nita.saml20/WebsiteDemo/WebsiteDemo.csproj
index a545bb4..e996f46 100644
--- a/src/dk.nita.saml20/WebsiteDemo/WebsiteDemo.csproj
+++ b/src/dk.nita.saml20/WebsiteDemo/WebsiteDemo.csproj
@@ -128,7 +128,8 @@
-
+
+
Designer
diff --git a/src/dk.nita.saml20/WebsiteDemo/idp-metadata/test-devtest4-idp-metadata.xml b/src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-devtest4-inttest-25-11-26.xml
similarity index 62%
rename from src/dk.nita.saml20/WebsiteDemo/idp-metadata/test-devtest4-idp-metadata.xml
rename to src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-devtest4-inttest-25-11-26.xml
index 850cc49..197f764 100644
--- a/src/dk.nita.saml20/WebsiteDemo/idp-metadata/test-devtest4-idp-metadata.xml
+++ b/src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-devtest4-inttest-25-11-26.xml
@@ -3,41 +3,43 @@
- MIIGYjCCBUqgAwIBAgIEX6YiGjANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJE
- SzESMBAGA1UECgwJVFJVU1QyNDA4MSYwJAYDVQQDDB1UUlVTVDI0MDggU3lzdGVt
- dGVzdCBYWFhJViBDQTAeFw0yMjA5MTQwNzIyMTBaFw0yNTA5MTQwNzIxNTdaMIGa
- MQswCQYDVQQGEwJESzExMC8GA1UECgwoRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2Vu
- IC8vIENWUjozNDA1MTE3ODFYMCAGA1UEBRMZQ1ZSOjM0MDUxMTc4LVVJRDo0MTIx
- OTIwNDA0BgNVBAMMLURpZ2l0YWxpc2VyaW5nc3N0eXJlbHNlbiAtIE5lbUxvZy1p
- biBJZFAgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIF+TEZl
- CFr6gdnEN1CJj8cPzsaDAoReDGtf4x5NfOaXdpimBGtRwX6Rbv79fAzJe5Ci5Wwn
- FdQrWk8ykXufyyjsKAtGc1EI4BRLXOW6slISZSN1GpI9l1DehE0I2J7t9Ov1crh7
- i1AnZDf58FRwLtnOI8FzO3bYvQoA3MNUMam58XwFtpnNbThacUfEkS4gAZx1sdND
- zosAdyj4uKZGfCkEkp6XTs26EOJzTBmEl6bQxxqZyD1/hzS8YgIF0807MvOmGXze
- oxP8IRF0bWNJtK9euCsUYWz0gfJtJqzgOFaPxEPrNo0ptvEjQHayZ81LvJXQocn7
- enl4Bix6AkUm02cCAwEAAaOCAv4wggL6MA4GA1UdDwEB/wQEAwIDuDCBlwYIKwYB
- BQUHAQEEgYowgYcwPAYIKwYBBQUHMAGGMGh0dHA6Ly9vY3NwLnN5c3RlbXRlc3Qz
- NC50cnVzdDI0MDguY29tL3Jlc3BvbmRlcjBHBggrBgEFBQcwAoY7aHR0cDovL3Yu
- YWlhLnN5c3RlbXRlc3QzNC50cnVzdDI0MDguY29tL3N5c3RlbXRlc3QzNC1jYS5j
- ZXIwggEgBgNVHSAEggEXMIIBEzCCAQ8GDSsGAQQBgfRRAgQGAwUwgf0wLwYIKwYB
- BQUHAgEWI2h0dHA6Ly93d3cudHJ1c3QyNDA4LmNvbS9yZXBvc2l0b3J5MIHJBggr
- BgEFBQcCAjCBvDAMFgVEYW5JRDADAgEBGoGrRGFuSUQgdGVzdCBjZXJ0aWZpa2F0
- ZXIgZnJhIGRlbm5lIENBIHVkc3RlZGVzIHVuZGVyIE9JRCAxLjMuNi4xLjQuMS4z
- MTMxMy4yLjQuNi4zLjUuIERhbklEIHRlc3QgY2VydGlmaWNhdGVzIGZyb20gdGhp
- cyBDQSBhcmUgaXNzdWVkIHVuZGVyIE9JRCAxLjMuNi4xLjQuMS4zMTMxMy4yLjQu
- Ni4zLjUuMC8GA1UdEQQoMCaBJHRob21hcy5ueW1hbmRAZXh0ZXJuYWwubmV4aWdy
- b3VwLmNvbTCBrQYDVR0fBIGlMIGiMDygOqA4hjZodHRwOi8vY3JsLnN5c3RlbXRl
- c3QzNC50cnVzdDI0MDguY29tL3N5c3RlbXRlc3QzNC5jcmwwYqBgoF6kXDBaMQsw
- CQYDVQQGEwJESzESMBAGA1UECgwJVFJVU1QyNDA4MSYwJAYDVQQDDB1UUlVTVDI0
- MDggU3lzdGVtdGVzdCBYWFhJViBDQTEPMA0GA1UEAwwGQ1JMNTc4MB8GA1UdIwQY
- MBaAFM1saJc5chmkNatk6vQRo4GH+Gk7MB0GA1UdDgQWBBTkIBjQrTbrUa+YkuWT
- mQcyrcaAlDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCltmRhlf28jwPZ
- nKz3exV2SotGGzmijDrK/1gndcx5ld8C556CjB8k3UplHSGJ1Sw60LGylvi33lsZ
- 6kLL7lIEtzokMFXouNFwIA5+b6/GF5r7G3hvxiyrzkZnRT7DCznV5cNtlGlZC4yM
- TQK4czYLnGGWutNtsKnrWMDoLsmHewNBetgPNt8hep/g3Xx5903gcOaitfT0VQkt
- iVZvLzveuqitWkHedzwVj+1QlBBIxaR+zmm0JccPIYUCv3xaI+TSDsOY1ROzkDaX
- uoHghyNk7qwsMPdnY50NmmAo68mnuEC7utRK/OqkGQ6UULHU9OzWxGzVljcQ5QLz
- Blt+6WFR
+
+ MIIGjDCCBMCgAwIBAgIUaGLv8eOYx3cubJyI3ymai6tAXCowQQYJKoZIhvcNAQEK
+ MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF
+ AKIDAgEgMGsxLTArBgNVBAMMJERlbiBEYW5za2UgU3RhdCBPQ0VTIHVkc3RlZGVu
+ ZGUtQ0EgMTETMBEGA1UECwwKVGVzdCAtIGN0aTEYMBYGA1UECgwPRGVuIERhbnNr
+ ZSBTdGF0MQswCQYDVQQGEwJESzAeFw0yNTA4MjAxMzQxNDBaFw0yODA4MTkxMzQx
+ MzlaMIGhMR0wGwYDVQQDDBROZW1Mb2ctaW4gSWRQIC0gVGVzdDE3MDUGA1UEBRMu
+ VUk6REstTzpHOmEwNDBmZTI2LWNlNzgtNDMyOC1hYzBlLTI5ZWViMTJmNDZkZjEh
+ MB8GA1UECgwYRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2VuMRcwFQYDVQRhDA5OVFJE
+ Sy0zNDA1MTE3ODELMAkGA1UEBhMCREswggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
+ ggGKAoIBgQCVUzcmIfK0NbJEmAQ7kFOTmXs1mkSHF0LAEFq7Ne8vpNz2H+0F5IyW
+ CwsBi3SByPTAEIClbIT6rLFBoMmIwjfRmAXd68zW7NYwR0N8PcR/hvtoCR1tW2Ju
+ KhQlKbWlaYLRvvvS3r5giIjHgEXpmv42caB76eYE3J2qWPaACOIRMXky1vCmbQpE
+ wqJr0S9+QDtMYh5ypVk1zX9rrY2TBbienruISBF1090uuzEQZtfiq9nZaBQAJZFl
+ cYLftepSZXW4QgJjWH6BhMrd4nvcz17WeabG3AhhKHvOUDukJgKMzQd/OcS1tY13
+ P7soMeT8D8h5bCnNDoCfqM/5mgxgxZZXnjJhzOCY+MdrXXBRST2uQLNg0gl/Lgqm
+ EKk0izQaMtNauR+5fFHSJMN23Ga6fAmHMlyEj+5JhGeScRvIBp9tPERPhRF0MdEj
+ obbcweMRWqurJYsdo9cIHwrL8WIgQGhQRUrKBu9VPZQTtVs3dn3cxZIJ3axu53hW
+ EKRwqw4UoasCAwEAAaOCAYcwggGDMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU
+ fyif2XGZQuJ159c1di5NCCVtdl4wewYIKwYBBQUHAQEEbzBtMEMGCCsGAQUFBzAC
+ hjdodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nlcy9pc3N1aW5nLzEvY2FjZXJ0L2lz
+ c3VpbmcuY2VyMCYGCCsGAQUFBzABhhpodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nz
+ cDAiBgNVHSAEGzAZMAgGBgQAj3oBATANBgsqgVCBKQEBAQMHATA7BggrBgEFBQcB
+ AwQvMC0wKwYIKwYBBQUHCwIwHwYHBACL7EkBAjAUhhJodHRwczovL3VpZC5nb3Yu
+ ZGswRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NhMS5jdGktZ292LmRrL29jZXMv
+ aXNzdWluZy8xL2NybC9pc3N1aW5nLmNybDAdBgNVHQ4EFgQU9KSlGZd1HK7yFmo+
+ zohr4oq6LLwwDgYDVR0PAQH/BAQDAgWgMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZI
+ AWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIBBQCiAwIBIAOCAYEA
+ Pt4QGIebeY3idMN6xPUQUBwYeiIjhtxqo2O9BHELEQ8Vp9fuBiExT286i1nNbd6T
+ BgFFuUPh2kuwUqAj4YfaG7aSiCToJrTuaxBGlMI9nci2l5VDJ/1/lVVT/OkMLgR9
+ xVXWSi8KFT8U9S+E1THE+G1YSNnt50yRDmqGNtG1XcaXQeu2Q4IcjMc2nf7NcGzG
+ widsKnBv0nNXwoYnh2ZLWPjQAgQzM3EYdZmEqaCrscYDFRbh4/T3TYjOo64N4rYZ
+ WTR+4/poz/29tyfVBWnr3rLuJH2q0kaiFVSXRHXoqOpCDUTVwsULShjmAHcPruQK
+ 7mRK1NQcwJtm0HQNWKUxizqc1pA38/9tU2vkQv+OxEEwHnCRK4XkLCJ8ZKCV5+c7
+ AehhTLtDHqsA93+8afgrTLe+kWEO+xRgOJxKV9TnqkvnYiG8BtCOcb4+6yUMGAoO
+ PULVKywIzutRsHYjZptCkuggBqNztSFU/DUd6SpSxf1MGfeh4SC4yPNvKQ+5fo7S
+
@@ -82,41 +84,43 @@
- MIIGYjCCBUqgAwIBAgIEX6YiGjANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJE
- SzESMBAGA1UECgwJVFJVU1QyNDA4MSYwJAYDVQQDDB1UUlVTVDI0MDggU3lzdGVt
- dGVzdCBYWFhJViBDQTAeFw0yMjA5MTQwNzIyMTBaFw0yNTA5MTQwNzIxNTdaMIGa
- MQswCQYDVQQGEwJESzExMC8GA1UECgwoRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2Vu
- IC8vIENWUjozNDA1MTE3ODFYMCAGA1UEBRMZQ1ZSOjM0MDUxMTc4LVVJRDo0MTIx
- OTIwNDA0BgNVBAMMLURpZ2l0YWxpc2VyaW5nc3N0eXJlbHNlbiAtIE5lbUxvZy1p
- biBJZFAgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIF+TEZl
- CFr6gdnEN1CJj8cPzsaDAoReDGtf4x5NfOaXdpimBGtRwX6Rbv79fAzJe5Ci5Wwn
- FdQrWk8ykXufyyjsKAtGc1EI4BRLXOW6slISZSN1GpI9l1DehE0I2J7t9Ov1crh7
- i1AnZDf58FRwLtnOI8FzO3bYvQoA3MNUMam58XwFtpnNbThacUfEkS4gAZx1sdND
- zosAdyj4uKZGfCkEkp6XTs26EOJzTBmEl6bQxxqZyD1/hzS8YgIF0807MvOmGXze
- oxP8IRF0bWNJtK9euCsUYWz0gfJtJqzgOFaPxEPrNo0ptvEjQHayZ81LvJXQocn7
- enl4Bix6AkUm02cCAwEAAaOCAv4wggL6MA4GA1UdDwEB/wQEAwIDuDCBlwYIKwYB
- BQUHAQEEgYowgYcwPAYIKwYBBQUHMAGGMGh0dHA6Ly9vY3NwLnN5c3RlbXRlc3Qz
- NC50cnVzdDI0MDguY29tL3Jlc3BvbmRlcjBHBggrBgEFBQcwAoY7aHR0cDovL3Yu
- YWlhLnN5c3RlbXRlc3QzNC50cnVzdDI0MDguY29tL3N5c3RlbXRlc3QzNC1jYS5j
- ZXIwggEgBgNVHSAEggEXMIIBEzCCAQ8GDSsGAQQBgfRRAgQGAwUwgf0wLwYIKwYB
- BQUHAgEWI2h0dHA6Ly93d3cudHJ1c3QyNDA4LmNvbS9yZXBvc2l0b3J5MIHJBggr
- BgEFBQcCAjCBvDAMFgVEYW5JRDADAgEBGoGrRGFuSUQgdGVzdCBjZXJ0aWZpa2F0
- ZXIgZnJhIGRlbm5lIENBIHVkc3RlZGVzIHVuZGVyIE9JRCAxLjMuNi4xLjQuMS4z
- MTMxMy4yLjQuNi4zLjUuIERhbklEIHRlc3QgY2VydGlmaWNhdGVzIGZyb20gdGhp
- cyBDQSBhcmUgaXNzdWVkIHVuZGVyIE9JRCAxLjMuNi4xLjQuMS4zMTMxMy4yLjQu
- Ni4zLjUuMC8GA1UdEQQoMCaBJHRob21hcy5ueW1hbmRAZXh0ZXJuYWwubmV4aWdy
- b3VwLmNvbTCBrQYDVR0fBIGlMIGiMDygOqA4hjZodHRwOi8vY3JsLnN5c3RlbXRl
- c3QzNC50cnVzdDI0MDguY29tL3N5c3RlbXRlc3QzNC5jcmwwYqBgoF6kXDBaMQsw
- CQYDVQQGEwJESzESMBAGA1UECgwJVFJVU1QyNDA4MSYwJAYDVQQDDB1UUlVTVDI0
- MDggU3lzdGVtdGVzdCBYWFhJViBDQTEPMA0GA1UEAwwGQ1JMNTc4MB8GA1UdIwQY
- MBaAFM1saJc5chmkNatk6vQRo4GH+Gk7MB0GA1UdDgQWBBTkIBjQrTbrUa+YkuWT
- mQcyrcaAlDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCltmRhlf28jwPZ
- nKz3exV2SotGGzmijDrK/1gndcx5ld8C556CjB8k3UplHSGJ1Sw60LGylvi33lsZ
- 6kLL7lIEtzokMFXouNFwIA5+b6/GF5r7G3hvxiyrzkZnRT7DCznV5cNtlGlZC4yM
- TQK4czYLnGGWutNtsKnrWMDoLsmHewNBetgPNt8hep/g3Xx5903gcOaitfT0VQkt
- iVZvLzveuqitWkHedzwVj+1QlBBIxaR+zmm0JccPIYUCv3xaI+TSDsOY1ROzkDaX
- uoHghyNk7qwsMPdnY50NmmAo68mnuEC7utRK/OqkGQ6UULHU9OzWxGzVljcQ5QLz
- Blt+6WFR
+
+ MIIGjDCCBMCgAwIBAgIUaGLv8eOYx3cubJyI3ymai6tAXCowQQYJKoZIhvcNAQEK
+ MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF
+ AKIDAgEgMGsxLTArBgNVBAMMJERlbiBEYW5za2UgU3RhdCBPQ0VTIHVkc3RlZGVu
+ ZGUtQ0EgMTETMBEGA1UECwwKVGVzdCAtIGN0aTEYMBYGA1UECgwPRGVuIERhbnNr
+ ZSBTdGF0MQswCQYDVQQGEwJESzAeFw0yNTA4MjAxMzQxNDBaFw0yODA4MTkxMzQx
+ MzlaMIGhMR0wGwYDVQQDDBROZW1Mb2ctaW4gSWRQIC0gVGVzdDE3MDUGA1UEBRMu
+ VUk6REstTzpHOmEwNDBmZTI2LWNlNzgtNDMyOC1hYzBlLTI5ZWViMTJmNDZkZjEh
+ MB8GA1UECgwYRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2VuMRcwFQYDVQRhDA5OVFJE
+ Sy0zNDA1MTE3ODELMAkGA1UEBhMCREswggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
+ ggGKAoIBgQCVUzcmIfK0NbJEmAQ7kFOTmXs1mkSHF0LAEFq7Ne8vpNz2H+0F5IyW
+ CwsBi3SByPTAEIClbIT6rLFBoMmIwjfRmAXd68zW7NYwR0N8PcR/hvtoCR1tW2Ju
+ KhQlKbWlaYLRvvvS3r5giIjHgEXpmv42caB76eYE3J2qWPaACOIRMXky1vCmbQpE
+ wqJr0S9+QDtMYh5ypVk1zX9rrY2TBbienruISBF1090uuzEQZtfiq9nZaBQAJZFl
+ cYLftepSZXW4QgJjWH6BhMrd4nvcz17WeabG3AhhKHvOUDukJgKMzQd/OcS1tY13
+ P7soMeT8D8h5bCnNDoCfqM/5mgxgxZZXnjJhzOCY+MdrXXBRST2uQLNg0gl/Lgqm
+ EKk0izQaMtNauR+5fFHSJMN23Ga6fAmHMlyEj+5JhGeScRvIBp9tPERPhRF0MdEj
+ obbcweMRWqurJYsdo9cIHwrL8WIgQGhQRUrKBu9VPZQTtVs3dn3cxZIJ3axu53hW
+ EKRwqw4UoasCAwEAAaOCAYcwggGDMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU
+ fyif2XGZQuJ159c1di5NCCVtdl4wewYIKwYBBQUHAQEEbzBtMEMGCCsGAQUFBzAC
+ hjdodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nlcy9pc3N1aW5nLzEvY2FjZXJ0L2lz
+ c3VpbmcuY2VyMCYGCCsGAQUFBzABhhpodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nz
+ cDAiBgNVHSAEGzAZMAgGBgQAj3oBATANBgsqgVCBKQEBAQMHATA7BggrBgEFBQcB
+ AwQvMC0wKwYIKwYBBQUHCwIwHwYHBACL7EkBAjAUhhJodHRwczovL3VpZC5nb3Yu
+ ZGswRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NhMS5jdGktZ292LmRrL29jZXMv
+ aXNzdWluZy8xL2NybC9pc3N1aW5nLmNybDAdBgNVHQ4EFgQU9KSlGZd1HK7yFmo+
+ zohr4oq6LLwwDgYDVR0PAQH/BAQDAgWgMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZI
+ AWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIBBQCiAwIBIAOCAYEA
+ Pt4QGIebeY3idMN6xPUQUBwYeiIjhtxqo2O9BHELEQ8Vp9fuBiExT286i1nNbd6T
+ BgFFuUPh2kuwUqAj4YfaG7aSiCToJrTuaxBGlMI9nci2l5VDJ/1/lVVT/OkMLgR9
+ xVXWSi8KFT8U9S+E1THE+G1YSNnt50yRDmqGNtG1XcaXQeu2Q4IcjMc2nf7NcGzG
+ widsKnBv0nNXwoYnh2ZLWPjQAgQzM3EYdZmEqaCrscYDFRbh4/T3TYjOo64N4rYZ
+ WTR+4/poz/29tyfVBWnr3rLuJH2q0kaiFVSXRHXoqOpCDUTVwsULShjmAHcPruQK
+ 7mRK1NQcwJtm0HQNWKUxizqc1pA38/9tU2vkQv+OxEEwHnCRK4XkLCJ8ZKCV5+c7
+ AehhTLtDHqsA93+8afgrTLe+kWEO+xRgOJxKV9TnqkvnYiG8BtCOcb4+6yUMGAoO
+ PULVKywIzutRsHYjZptCkuggBqNztSFU/DUd6SpSxf1MGfeh4SC4yPNvKQ+5fo7S
+
diff --git a/src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-inttest-25-11-28.xml b/src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-inttest-25-11-28.xml
new file mode 100644
index 0000000..cd25618
--- /dev/null
+++ b/src/dk.nita.saml20/WebsiteDemo/idp-metadata/oiosaml3-idp-inttest-25-11-28.xml
@@ -0,0 +1,199 @@
+
+
+
+
+
+
+ MIIGjDCCBMCgAwIBAgIUaGLv8eOYx3cubJyI3ymai6tAXCowQQYJKoZIhvcNAQEK
+ MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF
+ AKIDAgEgMGsxLTArBgNVBAMMJERlbiBEYW5za2UgU3RhdCBPQ0VTIHVkc3RlZGVu
+ ZGUtQ0EgMTETMBEGA1UECwwKVGVzdCAtIGN0aTEYMBYGA1UECgwPRGVuIERhbnNr
+ ZSBTdGF0MQswCQYDVQQGEwJESzAeFw0yNTA4MjAxMzQxNDBaFw0yODA4MTkxMzQx
+ MzlaMIGhMR0wGwYDVQQDDBROZW1Mb2ctaW4gSWRQIC0gVGVzdDE3MDUGA1UEBRMu
+ VUk6REstTzpHOmEwNDBmZTI2LWNlNzgtNDMyOC1hYzBlLTI5ZWViMTJmNDZkZjEh
+ MB8GA1UECgwYRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2VuMRcwFQYDVQRhDA5OVFJE
+ Sy0zNDA1MTE3ODELMAkGA1UEBhMCREswggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
+ ggGKAoIBgQCVUzcmIfK0NbJEmAQ7kFOTmXs1mkSHF0LAEFq7Ne8vpNz2H+0F5IyW
+ CwsBi3SByPTAEIClbIT6rLFBoMmIwjfRmAXd68zW7NYwR0N8PcR/hvtoCR1tW2Ju
+ KhQlKbWlaYLRvvvS3r5giIjHgEXpmv42caB76eYE3J2qWPaACOIRMXky1vCmbQpE
+ wqJr0S9+QDtMYh5ypVk1zX9rrY2TBbienruISBF1090uuzEQZtfiq9nZaBQAJZFl
+ cYLftepSZXW4QgJjWH6BhMrd4nvcz17WeabG3AhhKHvOUDukJgKMzQd/OcS1tY13
+ P7soMeT8D8h5bCnNDoCfqM/5mgxgxZZXnjJhzOCY+MdrXXBRST2uQLNg0gl/Lgqm
+ EKk0izQaMtNauR+5fFHSJMN23Ga6fAmHMlyEj+5JhGeScRvIBp9tPERPhRF0MdEj
+ obbcweMRWqurJYsdo9cIHwrL8WIgQGhQRUrKBu9VPZQTtVs3dn3cxZIJ3axu53hW
+ EKRwqw4UoasCAwEAAaOCAYcwggGDMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU
+ fyif2XGZQuJ159c1di5NCCVtdl4wewYIKwYBBQUHAQEEbzBtMEMGCCsGAQUFBzAC
+ hjdodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nlcy9pc3N1aW5nLzEvY2FjZXJ0L2lz
+ c3VpbmcuY2VyMCYGCCsGAQUFBzABhhpodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nz
+ cDAiBgNVHSAEGzAZMAgGBgQAj3oBATANBgsqgVCBKQEBAQMHATA7BggrBgEFBQcB
+ AwQvMC0wKwYIKwYBBQUHCwIwHwYHBACL7EkBAjAUhhJodHRwczovL3VpZC5nb3Yu
+ ZGswRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NhMS5jdGktZ292LmRrL29jZXMv
+ aXNzdWluZy8xL2NybC9pc3N1aW5nLmNybDAdBgNVHQ4EFgQU9KSlGZd1HK7yFmo+
+ zohr4oq6LLwwDgYDVR0PAQH/BAQDAgWgMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZI
+ AWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIBBQCiAwIBIAOCAYEA
+ Pt4QGIebeY3idMN6xPUQUBwYeiIjhtxqo2O9BHELEQ8Vp9fuBiExT286i1nNbd6T
+ BgFFuUPh2kuwUqAj4YfaG7aSiCToJrTuaxBGlMI9nci2l5VDJ/1/lVVT/OkMLgR9
+ xVXWSi8KFT8U9S+E1THE+G1YSNnt50yRDmqGNtG1XcaXQeu2Q4IcjMc2nf7NcGzG
+ widsKnBv0nNXwoYnh2ZLWPjQAgQzM3EYdZmEqaCrscYDFRbh4/T3TYjOo64N4rYZ
+ WTR+4/poz/29tyfVBWnr3rLuJH2q0kaiFVSXRHXoqOpCDUTVwsULShjmAHcPruQK
+ 7mRK1NQcwJtm0HQNWKUxizqc1pA38/9tU2vkQv+OxEEwHnCRK4XkLCJ8ZKCV5+c7
+ AehhTLtDHqsA93+8afgrTLe+kWEO+xRgOJxKV9TnqkvnYiG8BtCOcb4+6yUMGAoO
+ PULVKywIzutRsHYjZptCkuggBqNztSFU/DUd6SpSxf1MGfeh4SC4yPNvKQ+5fo7S
+
+
+
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MIIGjDCCBMCgAwIBAgIUaGLv8eOYx3cubJyI3ymai6tAXCowQQYJKoZIhvcNAQEK
+ MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF
+ AKIDAgEgMGsxLTArBgNVBAMMJERlbiBEYW5za2UgU3RhdCBPQ0VTIHVkc3RlZGVu
+ ZGUtQ0EgMTETMBEGA1UECwwKVGVzdCAtIGN0aTEYMBYGA1UECgwPRGVuIERhbnNr
+ ZSBTdGF0MQswCQYDVQQGEwJESzAeFw0yNTA4MjAxMzQxNDBaFw0yODA4MTkxMzQx
+ MzlaMIGhMR0wGwYDVQQDDBROZW1Mb2ctaW4gSWRQIC0gVGVzdDE3MDUGA1UEBRMu
+ VUk6REstTzpHOmEwNDBmZTI2LWNlNzgtNDMyOC1hYzBlLTI5ZWViMTJmNDZkZjEh
+ MB8GA1UECgwYRGlnaXRhbGlzZXJpbmdzc3R5cmVsc2VuMRcwFQYDVQRhDA5OVFJE
+ Sy0zNDA1MTE3ODELMAkGA1UEBhMCREswggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
+ ggGKAoIBgQCVUzcmIfK0NbJEmAQ7kFOTmXs1mkSHF0LAEFq7Ne8vpNz2H+0F5IyW
+ CwsBi3SByPTAEIClbIT6rLFBoMmIwjfRmAXd68zW7NYwR0N8PcR/hvtoCR1tW2Ju
+ KhQlKbWlaYLRvvvS3r5giIjHgEXpmv42caB76eYE3J2qWPaACOIRMXky1vCmbQpE
+ wqJr0S9+QDtMYh5ypVk1zX9rrY2TBbienruISBF1090uuzEQZtfiq9nZaBQAJZFl
+ cYLftepSZXW4QgJjWH6BhMrd4nvcz17WeabG3AhhKHvOUDukJgKMzQd/OcS1tY13
+ P7soMeT8D8h5bCnNDoCfqM/5mgxgxZZXnjJhzOCY+MdrXXBRST2uQLNg0gl/Lgqm
+ EKk0izQaMtNauR+5fFHSJMN23Ga6fAmHMlyEj+5JhGeScRvIBp9tPERPhRF0MdEj
+ obbcweMRWqurJYsdo9cIHwrL8WIgQGhQRUrKBu9VPZQTtVs3dn3cxZIJ3axu53hW
+ EKRwqw4UoasCAwEAAaOCAYcwggGDMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU
+ fyif2XGZQuJ159c1di5NCCVtdl4wewYIKwYBBQUHAQEEbzBtMEMGCCsGAQUFBzAC
+ hjdodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nlcy9pc3N1aW5nLzEvY2FjZXJ0L2lz
+ c3VpbmcuY2VyMCYGCCsGAQUFBzABhhpodHRwOi8vY2ExLmN0aS1nb3YuZGsvb2Nz
+ cDAiBgNVHSAEGzAZMAgGBgQAj3oBATANBgsqgVCBKQEBAQMHATA7BggrBgEFBQcB
+ AwQvMC0wKwYIKwYBBQUHCwIwHwYHBACL7EkBAjAUhhJodHRwczovL3VpZC5nb3Yu
+ ZGswRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NhMS5jdGktZ292LmRrL29jZXMv
+ aXNzdWluZy8xL2NybC9pc3N1aW5nLmNybDAdBgNVHQ4EFgQU9KSlGZd1HK7yFmo+
+ zohr4oq6LLwwDgYDVR0PAQH/BAQDAgWgMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZI
+ AWUDBAIBBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIBBQCiAwIBIAOCAYEA
+ Pt4QGIebeY3idMN6xPUQUBwYeiIjhtxqo2O9BHELEQ8Vp9fuBiExT286i1nNbd6T
+ BgFFuUPh2kuwUqAj4YfaG7aSiCToJrTuaxBGlMI9nci2l5VDJ/1/lVVT/OkMLgR9
+ xVXWSi8KFT8U9S+E1THE+G1YSNnt50yRDmqGNtG1XcaXQeu2Q4IcjMc2nf7NcGzG
+ widsKnBv0nNXwoYnh2ZLWPjQAgQzM3EYdZmEqaCrscYDFRbh4/T3TYjOo64N4rYZ
+ WTR+4/poz/29tyfVBWnr3rLuJH2q0kaiFVSXRHXoqOpCDUTVwsULShjmAHcPruQK
+ 7mRK1NQcwJtm0HQNWKUxizqc1pA38/9tU2vkQv+OxEEwHnCRK4XkLCJ8ZKCV5+c7
+ AehhTLtDHqsA93+8afgrTLe+kWEO+xRgOJxKV9TnqkvnYiG8BtCOcb4+6yUMGAoO
+ PULVKywIzutRsHYjZptCkuggBqNztSFU/DUd6SpSxf1MGfeh4SC4yPNvKQ+5fo7S
+
+
+
+
+
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NemLog-in
+
+
+ nemlogin@digst.dk
+
+
+
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Bindings/HttpRedirectBindingBuilder.cs b/src/dk.nita.saml20/dk.nita.saml20/Bindings/HttpRedirectBindingBuilder.cs
index 1d2bc8f..2ad1ad6 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Bindings/HttpRedirectBindingBuilder.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Bindings/HttpRedirectBindingBuilder.cs
@@ -78,8 +78,9 @@ public AsymmetricAlgorithm signingKey
set
{
// Check if the key is of a supported type. [SAMLBind] sect. 3.4.4.1 specifies this.
- if (!(value is RSACryptoServiceProvider || value == null))
- throw new ArgumentException("Signing key must be an instance of RSACryptoServiceProvider.");
+ // Accept any RSA implementation (RSACryptoServiceProvider for legacy CSP keys, RSACng for CNG keys).
+ if (!(value is RSA || value == null))
+ throw new ArgumentException("Signing key must be an instance of RSA.");
_signingKey = value;
}
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha1SignatureProvider.cs b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha1SignatureProvider.cs
index 11650e2..032c2d9 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha1SignatureProvider.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha1SignatureProvider.cs
@@ -7,16 +7,7 @@ internal class RsaSha1SignatureProvider : SignatureProvider
{
public override string SignatureUri => SignedXml.XmlDsigRSASHA1Url;
public override string DigestUri => SignedXml.XmlDsigSHA1Url;
- protected override byte[] SignDataIntern(RSACryptoServiceProvider key, byte[] data)
- {
- return key.SignData(data, new SHA1CryptoServiceProvider());
- }
-
- protected override bool VerifySignatureIntern(RSACryptoServiceProvider key, byte[] data, byte[] signature)
- {
- var hash = new SHA1Managed().ComputeHash(data);
- return ((RSACryptoServiceProvider) key).VerifyHash(hash, "SHA1", signature);
- }
+ protected override HashAlgorithmName HashName => HashAlgorithmName.SHA1;
}
}
\ No newline at end of file
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha256SignatureProvider.cs b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha256SignatureProvider.cs
index 6185ca7..3d4d3e2 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha256SignatureProvider.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha256SignatureProvider.cs
@@ -7,15 +7,6 @@ internal class RsaSha256SignatureProvider : SignatureProvider
{
public override string SignatureUri => SignedXml.XmlDsigRSASHA256Url;
public override string DigestUri => SignedXml.XmlDsigSHA256Url;
- protected override byte[] SignDataIntern(RSACryptoServiceProvider key, byte[] data)
- {
- return key.SignData(data, new SHA256CryptoServiceProvider());
- }
-
- protected override bool VerifySignatureIntern(RSACryptoServiceProvider key, byte[] data, byte[] signature)
- {
- var hash = new SHA256Managed().ComputeHash(data);
- return ((RSACryptoServiceProvider)key).VerifyHash(hash, "SHA256", signature);
- }
+ protected override HashAlgorithmName HashName => HashAlgorithmName.SHA256;
}
}
\ No newline at end of file
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha512SignatureProvider.cs b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha512SignatureProvider.cs
index c2b1b86..a081f17 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha512SignatureProvider.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/RsaSha512SignatureProvider.cs
@@ -7,15 +7,6 @@ internal class RsaSha512SignatureProvider : SignatureProvider
{
public override string SignatureUri => SignedXml.XmlDsigRSASHA512Url;
public override string DigestUri => SignedXml.XmlDsigSHA512Url;
- protected override byte[] SignDataIntern(RSACryptoServiceProvider key, byte[] data)
- {
- return key.SignData(data, new SHA512CryptoServiceProvider());
- }
-
- protected override bool VerifySignatureIntern(RSACryptoServiceProvider key, byte[] data, byte[] signature)
- {
- var hash = new SHA512Managed().ComputeHash(data);
- return ((RSACryptoServiceProvider)key).VerifyHash(hash, "SHA512", signature);
- }
+ protected override HashAlgorithmName HashName => HashAlgorithmName.SHA512;
}
}
\ No newline at end of file
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/SignatureProvider.cs b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/SignatureProvider.cs
index 8fed0a7..0b823a1 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/SignatureProvider.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Bindings/SignatureProviders/SignatureProvider.cs
@@ -15,25 +15,22 @@ internal abstract class SignatureProvider : ISignatureProvider
public byte[] SignData(AsymmetricAlgorithm key, byte[] data)
{
- var rsa = (RSACryptoServiceProvider)key;
+ var rsa = ToSha2CapableRsa(key);
- rsa = ConvertProviderType(rsa);
-
- return SignDataIntern(rsa, data);
+ return rsa.SignData(data, HashName, RSASignaturePadding.Pkcs1);
}
- protected abstract byte[] SignDataIntern(RSACryptoServiceProvider key, byte[] data);
-
public bool VerifySignature(AsymmetricAlgorithm key, byte[] data, byte[] signature)
{
- var rsa = (RSACryptoServiceProvider)key;
-
- rsa = ConvertProviderType(rsa);
+ var rsa = ToSha2CapableRsa(key);
- return VerifySignatureIntern(rsa, data, signature);
+ return rsa.VerifyData(data, signature, HashName, RSASignaturePadding.Pkcs1);
}
- protected abstract bool VerifySignatureIntern(RSACryptoServiceProvider key, byte[] data, byte[] signature);
+ ///
+ /// The hash algorithm this provider uses when signing/verifying raw data such as the HTTP-Redirect query.
+ ///
+ protected abstract HashAlgorithmName HashName { get; }
public void SignAssertion(XmlDocument doc, string id, X509Certificate2 cert)
{
@@ -72,26 +69,52 @@ private SignedXml Sign(XmlDocument doc, string id, X509Certificate2 cert)
return signedXml;
}
- private static RSACryptoServiceProvider ConvertProviderType(RSACryptoServiceProvider rsa)
+ ///
+ /// Returns an instance guaranteed to support SHA-2, keeping the signing/verification
+ /// path provider-agnostic: it works with modern CNG keys () as well as legacy
+ /// CryptoAPI keys ().
+ ///
+ private static RSA ToSha2CapableRsa(AsymmetricAlgorithm key)
{
- // ProviderType == 1 is PROV_RSA_FULL provider type that only supports SHA1. Change it to PROV_RSA_AES=24 that supports SHA2 also.
+ var rsa = (RSA)key;
+
+ // CNG keys already support SHA-2.
+ if (rsa is RSACng)
+ return rsa;
+
+ var csp = rsa as RSACryptoServiceProvider;
+ if (csp == null)
+ return rsa;
+
+ // Public keys (inbound signature verification): re-import into a CNG key, which supports
+ // SHA-2 regardless of the originating CSP provider type.
+ if (csp.PublicOnly)
+ {
+ var cng = new RSACng();
+ cng.ImportParameters(csp.ExportParameters(false));
+ return cng;
+ }
+
+ // A private key backed by a legacy PROV_RSA_FULL (provider type 1) CSP only supports SHA-1.
+ // Reopen the same key container under PROV_RSA_AES (provider type 24), which supports SHA-2.
// https://github.com/Microsoft/referencesource/blob/master/System.IdentityModel/System/IdentityModel/Tokens/X509AsymmetricSecurityKey.cs#L54
- if (rsa != null && rsa.CspKeyContainerInfo.ProviderType == 1)
+ if (csp.CspKeyContainerInfo.ProviderType == 1)
{
if (Trace.ShouldTrace(TraceEventType.Verbose))
+ Trace.TraceData(TraceEventType.Verbose, "Reopened RSA private key under provider type 24 to enable SHA-2");
+
+ var cspParams = new CspParameters
{
- Trace.TraceData(TraceEventType.Verbose,
- "Changed provider type from " + rsa.CspKeyContainerInfo.ProviderType + " to 24");
- }
- CspParameters csp = new CspParameters();
- csp.ProviderType = 24;
- csp.KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName;
- csp.KeyNumber = (int)rsa.CspKeyContainerInfo.KeyNumber;
- if (rsa.CspKeyContainerInfo.MachineKeyStore)
- csp.Flags = CspProviderFlags.UseMachineKeyStore;
- csp.Flags |= CspProviderFlags.UseExistingKey;
- rsa = new RSACryptoServiceProvider(csp);
+ ProviderType = 24,
+ KeyContainerName = csp.CspKeyContainerInfo.KeyContainerName,
+ KeyNumber = (int)csp.CspKeyContainerInfo.KeyNumber
+ };
+ if (csp.CspKeyContainerInfo.MachineKeyStore)
+ cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
+ cspParams.Flags |= CspProviderFlags.UseExistingKey;
+ return new RSACryptoServiceProvider(cspParams);
}
+
return rsa;
}
}
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20LogoutHandler.cs b/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20LogoutHandler.cs
index 57fdcb0..f2bda1d 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20LogoutHandler.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20LogoutHandler.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Web;
using System.Web.Security;
@@ -302,7 +303,7 @@ private void TransferClient(IDPEndPoint endpoint, HttpContext context)
if (destination.Binding == SAMLBinding.REDIRECT)
{
HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder();
- builder.signingKey = FederationConfig.GetConfig().GetFirstValidCertificate().PrivateKey;
+ builder.signingKey = FederationConfig.GetConfig().GetFirstValidCertificate().GetRSAPrivateKey();
request.Destination = destination.Url;
request.Reason = Saml20Constants.Reasons.User;
request.SubjectToLogOut.Value = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value;
@@ -589,7 +590,7 @@ private void HandleRequest(HttpContext context)
HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder();
builder.RelayState = context.Request.Params["RelayState"];
builder.Response = response.GetXml().OuterXml;
- builder.signingKey = FederationConfig.GetConfig().GetFirstValidCertificate().PrivateKey;
+ builder.signingKey = FederationConfig.GetConfig().GetFirstValidCertificate().GetRSAPrivateKey();
builder.ShaHashingAlgorithm = shaHashingAlgorithm;
string s = destination.Url + "?" + builder.ToQuery();
context.Response.Redirect(s, true);
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20SignonHandler.cs b/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20SignonHandler.cs
index 01c1b98..ce21363 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20SignonHandler.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Protocol/Saml20SignonHandler.cs
@@ -409,7 +409,7 @@ private static Saml20EncryptedAssertion GetDecryptedAssertion(XmlElement elem)
{
var tryDecryptAssertion = new Func((certificate) =>
{
- Saml20EncryptedAssertion decryptedAssertion = new Saml20EncryptedAssertion((RSA)certificate.PrivateKey);
+ Saml20EncryptedAssertion decryptedAssertion = new Saml20EncryptedAssertion(certificate.GetRSAPrivateKey());
decryptedAssertion.LoadXml(elem);
decryptedAssertion.Decrypt();
return decryptedAssertion;
@@ -893,7 +893,7 @@ private void TransferClient(IDPEndPoint idpEndpoint, Saml20AuthnRequest request,
Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Redirect, idpEndpoint.Id));
HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder();
- builder.signingKey = _certificate.PrivateKey;
+ builder.signingKey = _certificate.GetRSAPrivateKey();
builder.Request = request.GetXml().OuterXml;
builder.ShaHashingAlgorithm = shaHashingAlgorithm;
string s = request.Destination + "?" + builder.ToQuery();
diff --git a/src/dk.nita.saml20/dk.nita.saml20/Saml20AttributeQuery.cs b/src/dk.nita.saml20/dk.nita.saml20/Saml20AttributeQuery.cs
index f3acd69..bbd12a3 100644
--- a/src/dk.nita.saml20/dk.nita.saml20/Saml20AttributeQuery.cs
+++ b/src/dk.nita.saml20/dk.nita.saml20/Saml20AttributeQuery.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
using System.Web;
using System.Xml;
using dk.nita.saml20.Bindings;
@@ -220,7 +221,7 @@ public void PerformQuery(HttpContext context, IDPEndPoint endPoint, string nameI
{
Saml20EncryptedAssertion ass =
new Saml20EncryptedAssertion(
- (RSA)FederationConfig.GetConfig().GetFirstValidCertificate().PrivateKey);
+ FederationConfig.GetConfig().GetFirstValidCertificate().GetRSAPrivateKey());
ass.LoadXml(xmlAssertion);
ass.Decrypt();
xmlAssertion = ass.Assertion.DocumentElement;