diff --git a/AssemblyInfoFileVersion.cs b/AssemblyInfoFileVersion.cs index 399343609dd146418f15532cb8813c1cf35f41d8..b028aaebbbcb5b719730a419b7b35ca3ff84ce5a 100644 --- a/AssemblyInfoFileVersion.cs +++ b/AssemblyInfoFileVersion.cs @@ -10,5 +10,5 @@ using System.Reflection; // //------------------------------------------------------------------------------ -[assembly: AssemblyFileVersionAttribute("3.0.0.BETA")] +[assembly: AssemblyFileVersionAttribute("3.0.0.65534")] diff --git a/common b/common index 68cedda61fcbc2e27b952e3d5ebacc8be5bbe3eb..364cc3194f6e46accbd0025fc628898a4a55f4b8 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 68cedda61fcbc2e27b952e3d5ebacc8be5bbe3eb +Subproject commit 364cc3194f6e46accbd0025fc628898a4a55f4b8 diff --git a/src/dk.gov.oiosi.raspProfile/DefaultLdapConfig.cs b/src/dk.gov.oiosi.raspProfile/DefaultLdapConfig.cs index 942b5ea9ab193bf49e53a51634a0abc40424e5b8..b9b827a468182904a72cd5f98564011a83b60184 100644 --- a/src/dk.gov.oiosi.raspProfile/DefaultLdapConfig.cs +++ b/src/dk.gov.oiosi.raspProfile/DefaultLdapConfig.cs @@ -93,7 +93,7 @@ namespace dk.gov.oiosi.raspProfile ldapSettings.CertificateInfrastructures = new CertificateInfrastructure[] { new CertificateInfrastructure{Id = "NemID", Host = "crtdir.certifikat.dk", Port = 389 }, - new CertificateInfrastructure{Id = "MitID", Host = "ldap.ca1.gov.dk", Port = 389 } + new CertificateInfrastructure{Id = "MitID", Host = "ca1.cti-gov.dk", Port = 389 } }; ldapSettings.ConnectionTimeoutMsec = 5000; diff --git a/src/dk.gov.oiosi.raspProfile/DefaultRootCertificateCollectionConfig.cs b/src/dk.gov.oiosi.raspProfile/DefaultRootCertificateCollectionConfig.cs index 97c2afa25da298626b5648d4dc1338f15d9e2fc9..c60ae3528e52df85630b38947c64ffd666028682 100644 --- a/src/dk.gov.oiosi.raspProfile/DefaultRootCertificateCollectionConfig.cs +++ b/src/dk.gov.oiosi.raspProfile/DefaultRootCertificateCollectionConfig.cs @@ -70,6 +70,14 @@ namespace dk.gov.oiosi.raspProfile certificatLocation.StoreLocation = StoreLocation.LocalMachine; certificatLocation.StoreName = StoreName.Root; rootCertificateCollectionConfig.GetAsList().Add(certificatLocation); + + // MitId + certificatLocation = new RootCertificateLocation(); + certificatLocation.Description = "Den Danske Stat OCES rod-CA"; + certificatLocation.SerialNumber = "573f57e67530f1a0777dfbc69f090438d3360256"; + certificatLocation.StoreLocation = StoreLocation.LocalMachine; + certificatLocation.StoreName = StoreName.Root; + rootCertificateCollectionConfig.GetAsList().Add(certificatLocation); } /// diff --git a/src/dk.gov.oiosi.resource/RaspConfiguration.Live.xml b/src/dk.gov.oiosi.resource/RaspConfiguration.Live.xml index 4e617a38e1817c28b5e50510b51b86f2ef0a956b..8177ff54f504a35ab50d5025dc4bccc08f3b3ec8 100644 --- a/src/dk.gov.oiosi.resource/RaspConfiguration.Live.xml +++ b/src/dk.gov.oiosi.resource/RaspConfiguration.Live.xml @@ -4564,7 +4564,7 @@ ​389 ​ - ​ldap.ca1.gov.dk + ​ca1.gov.dk ​389 ​5000 diff --git a/src/dk.gov.oiosi.resource/RaspConfiguration.Test.xml b/src/dk.gov.oiosi.resource/RaspConfiguration.Test.xml index 9eba5447701b9be31d623ef1ac6d9d5573d2e6be..ed20afea05a8edc12fa76325099e67765bbea893 100644 --- a/src/dk.gov.oiosi.resource/RaspConfiguration.Test.xml +++ b/src/dk.gov.oiosi.resource/RaspConfiguration.Test.xml @@ -4565,7 +4565,7 @@ ​389 ​ - ​ldap.ca1.cti-gov.dk + ​ca1.cti-gov.dk ​389 ​5000 @@ -4775,8 +4775,8 @@ LocalMachine Root - 4b ea 6e 94 - TRUST2408 Systemtest VII Primary CA + 573f57e67530f1a0777dfbc69f090438d3360256 + Den Danske Stat OCES rod-CA diff --git a/src/dk.gov.oiosi/security/revocation/crl/CertificateUtil.cs b/src/dk.gov.oiosi/security/revocation/crl/CertificateUtil.cs index 6b0e447fc7a72072fc3fed0404865352e009e579..c27137ba258ee14dff662f4a9f7ac4faf8155b25 100644 --- a/src/dk.gov.oiosi/security/revocation/crl/CertificateUtil.cs +++ b/src/dk.gov.oiosi/security/revocation/crl/CertificateUtil.cs @@ -10,6 +10,79 @@ namespace dk.gov.oiosi.security.revocation.crl { public class CertificateUtil { + public X509Certificate2 FindIssuerCertificate(X509Certificate2 serverX509Certificate2) + { + X509Certificate2 issuerX509Certificate2 = null; + + // Find the issuer certificate + X509Chain x509Chain = new X509Chain(); + x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + x509Chain.Build(serverX509Certificate2); + + // Iterate though the chain, to validate if it contain a valid root vertificate + X509ChainElementCollection x509ChainElementCollection = x509Chain.ChainElements; + X509ChainElementEnumerator enumerator = x509ChainElementCollection.GetEnumerator(); + X509ChainElement x509ChainElement; + X509Certificate2 x509Certificate2 = null; + IDictionary map = new Dictionary(); + + // At this point, the certificate is not valid, until a + // it is proved that it has a valid root certificate + while (enumerator.MoveNext()) + { + x509ChainElement = enumerator.Current; + x509Certificate2 = x509ChainElement.Certificate; + map.Add(x509Certificate2.Subject, x509Certificate2); + } + + if (map.ContainsKey(serverX509Certificate2.Issuer)) + { + issuerX509Certificate2 = map[serverX509Certificate2.Issuer]; + } + + return issuerX509Certificate2; + } + + /* public X509Certificate2 FindRootCertificate(X509Certificate2 serverX509Certificate2, IDictionary rootCertificateDirectory) + { + bool rootCertificateFound = false; + X509Certificate2 desiredRootX509Certificate2 = null; + // Find the desired root certificate + X509Chain x509Chain = new X509Chain(); + x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + x509Chain.Build(serverX509Certificate2); + + // Iterate though the chain, to validate if it contain a valid root vertificate + X509ChainElementCollection x509ChainElementCollection = x509Chain.ChainElements; + X509ChainElementEnumerator enumerator = x509ChainElementCollection.GetEnumerator(); + X509ChainElement x509ChainElement; + X509Certificate2 x509Certificate2 = null; + string x509CertificateThumbprint; + // At this point, the certificate is not valid, until a + // it is proved that it has a valid root certificate + while (rootCertificateFound == false && enumerator.MoveNext()) + { + x509ChainElement = enumerator.Current; + x509Certificate2 = x509ChainElement.Certificate; + x509CertificateThumbprint = x509Certificate2.Thumbprint.ToLowerInvariant(); + if (rootCertificateDirectory.ContainsKey(x509CertificateThumbprint)) + { + // The current chain element is in the trusted rootCertificateDirectory + rootCertificateFound = true; + + // now the loop will break, as we have found a trusted root certificate + } + } + + if (rootCertificateFound) + { + // root certificate is found + desiredRootX509Certificate2 = x509Certificate2; + } + + return desiredRootX509Certificate2; + }*/ + /// /// Gets a list of URLs from the specified certificate. /// diff --git a/src/dk.gov.oiosi/security/revocation/ocsp/OcspLookup.cs b/src/dk.gov.oiosi/security/revocation/ocsp/OcspLookup.cs index 4211bef4f2a637dd61f3afd570f2c6530879fdb7..1127ea636f1f33fe3e41e49c1fbff55ae21ddedb 100644 --- a/src/dk.gov.oiosi/security/revocation/ocsp/OcspLookup.cs +++ b/src/dk.gov.oiosi/security/revocation/ocsp/OcspLookup.cs @@ -1,871 +1,806 @@ -/* - * The contents of this file are subject to the Mozilla Public - * License Version 1.1 (the "License"); you may not use this - * file except in compliance with the License. You may obtain - * a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * - * The Original Code is .NET RASP toolkit. - * - * The Initial Developer of the Original Code is Accenture and Avanade. - * Portions created by Accenture and Avanade are Copyright (C) 2009 - * Danish National IT and Telecom Agency (http://www.itst.dk). - * All Rights Reserved. - * - * Contributor(s): - * Gert Sylvest, Avanade - * Jesper Jensen, Avanade - * Ramzi Fadel, Avanade - * Patrik Johansson, Accenture - * Dennis Søgaard, Accenture - * Christian Pedersen, Accenture - * Martin Bentzen, Accenture - * Mikkel Hippe Brun, ITST - * Finn Hartmann Jordal, ITST - * Christian Lanng, ITST - * - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text.RegularExpressions; -using dk.gov.oiosi.common; -using dk.gov.oiosi.common.cache; -using dk.gov.oiosi.configuration; -using Org.BouncyCastle.Ocsp; -using Org.BouncyCastle.Asn1.Ocsp; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.X509; -using dk.gov.oiosi.security.revocation.crl; -using Org.BouncyCastle.Security.Certificates; -using dk.gov.oiosi.logging; -//using Novell.Directory.Ldap.Asn1; - -namespace dk.gov.oiosi.security.revocation.ocsp { - - /// - /// Class for checking certificate revocation status against an OCSP (Online Certificate Status Protocol) server. - /// - public class OcspLookup : IRevocationLookup - { - private ILogger logger; - private OcspConfig _configuration; - - private readonly int BufferSize = 4096 * 8; - - /// - /// List of the default OCES (OCES1 and OCES2) root certificate - /// - //private IList defaultOcesRootCertificateList; - - private IDictionary rootCertificateDirectory; - - private ICache ocspCache; - - /// - /// Instantiates OcspLookup and loads the OCES default root certificate - /// - /// Configuration parameters - public OcspLookup(OcspConfig configuration) - { - this.logger = LoggerFactory.Create(this); - this.Init(configuration, null); - } - - /// - /// Constructor - /// - /// Configuration - /// The default root certificate - public OcspLookup(OcspConfig configuration, IList defaultRootCertificateList) - { - this.logger = LoggerFactory.Create(this); - this.Init(configuration, defaultRootCertificateList); - } - - /// - /// Default constructor. Attempts to load configuration from configuration file. - /// - public OcspLookup() - { - this.logger = LoggerFactory.Create(this); - OcspConfig configuration = ConfigurationHandler.GetConfigurationSection(); - this.Init(configuration, null); - } - - - /// - /// Gets the configuration of the lookup client - /// - public OcspConfig Configuration - { - get { return _configuration; } - } - - /// - /// To be able to call the CheckCertificate method asynchron, we create a delegate. - /// - /// the certificate to check - /// the OcSpResponse object to store the result - public delegate RevocationResponse AsyncOcspCall(X509Certificate2 certificate); - - /// - /// Initializes. If the default root certificate is set to null, an attempt is made - /// to get a default root certificate from a Configuration.xml file - /// - /// OCSP configuration - /// If the default root certificate is set to null, an attempt is made - /// to get a default root certificate from a Configuration.xml file - private void Init(OcspConfig configuration, IList defaultRootCertificateList) - { - this.ocspCache = CacheFactory.Instance.OcspLookupCache; - try - { - // 1. Set configuration - _configuration = configuration; - - // 2. Get default certificate: - if (defaultRootCertificateList == null) - { - defaultRootCertificateList = _configuration.GetDefaultOcesRootCertificateListFromStore(); - } - - // put the root certificates into the directory - - this.rootCertificateDirectory = new Dictionary(); - - foreach (X509Certificate2 x509Certificate2 in defaultRootCertificateList) - { - this.rootCertificateDirectory.Add(x509Certificate2.Thumbprint.ToLowerInvariant(), x509Certificate2); - } - } - catch (UriFormatException) - { - throw; - } - catch (ArgumentNullException) - { - throw; - } - catch (OverflowException) - { - throw; - } - catch (FormatException) - { - throw; - } - catch (CryptographicUnexpectedOperationException) - { - throw; - } - catch (CryptographicException) - { - throw; - } - catch (Exception) - { - throw; - } - } - - /// - /// Checks a certificate status on a ocsp server - /// - /// The certificate to check - /// The RevocationResponse object that contains the result - /// This exception is thrown, if an unexpected exception is thrown during the method - private RevocationResponse RevocationResponse(X509Certificate2 x509Certificate2) - { - // this method can be call requsiv, so check the cache first - RevocationResponse revocationResponse; - - this.logger.Debug(string.Format("OCSP validation the certificate '{0}'.", x509Certificate2.SubjectName.Name)); - - bool ocspResponseExistsInCache = this.ocspCache.TryGetValue(x509Certificate2.Thumbprint.ToLowerInvariant(), out revocationResponse); - if (ocspResponseExistsInCache) - { - // response already in cache. - // Check if the response still is valid - if (revocationResponse.NextUpdate < DateTime.Now) - { - // the cached value is to old - // get new value - revocationResponse = this.RevocationResponseOnline(x509Certificate2); - } - } - else - { - // respons is not in cache - revocationResponse = this.RevocationResponseOnline(x509Certificate2); - } - - if (this.logger.IsInfoEnabled) - { - if (!revocationResponse.IsValid) - { - this.logger.Info(string.Format("Certificate '{0}' is revoked.", x509Certificate2.SubjectName.Name)); - } - } - - return revocationResponse; - } - - /// - /// Checks a certificate status on a ocsp server - /// - /// The certificate to check - /// The RevocationResponse object that contains the result - /// This exception is thrown, if an unexpected exception is thrown during the method - private RevocationResponse RevocationResponseOnline(X509Certificate2 x509Certificate2) - { - RevocationResponse revocationResponse = new RevocationResponse(); - - if (x509Certificate2 == null) - { - throw new CheckCertificateOcspUnexpectedException(); - } - // http://bouncy-castle.1462172.n4.nabble.com/c-ocsp-verification-td3160243.html - X509Certificate2 issuerX509Certificate2 = this.FindIssuerCertificate(x509Certificate2); - - if (issuerX509Certificate2 == null) - { - throw new CheckCertificateOcspUnexpectedException("Issuer certificate '"+ x509Certificate2.Issuer +"' not found."); - } - - if (issuerX509Certificate2.Thumbprint.Equals(x509Certificate2.Thumbprint, StringComparison.OrdinalIgnoreCase)) - { - // the certificate and the issuer certificace is the same - // this mean that the root certificate is not trusted - revocationResponse = null; - } - else - { - revocationResponse = this.RevocationResponseOnline(x509Certificate2, issuerX509Certificate2); - - if (revocationResponse != null) - { - if (revocationResponse.Exception == null) - { - // no exception recorded - if (revocationResponse.IsValid) - { - // now we know the certificate is valid. - // if the issuer is a trusted root certificate, all is good - if (this.rootCertificateDirectory.ContainsKey(issuerX509Certificate2.Thumbprint.ToLowerInvariant())) - { - // the root certificate is trusted, so the RevocationResponse can be put on the cache - this.ocspCache.Set(x509Certificate2.Thumbprint.ToLowerInvariant(), revocationResponse); - } - else - { - // we do not yet know if the certificate is valid. - // the certificate migth be good, but if the issueing certificate is revoked, - // then the certificate should also be revoked. - // Validate the issuer certificate - // this is required, because certificate can have a chain that is longer then 2 - - // The only problem is, that we can not ocsp validate the intermiddel certificate (the issuer certificate). - // acording to DanID - that certificate can only be validated with CRL - // Note : The crl list will be/should be very short. Only containing the issuer certificate that has been revoked. - // A good guess is that there at all time will be most 10 issuer certificate, so the list of revoked issuer certificate is short. - IList issuerUrl = this.GetAuthorityInformationAccessOcspUrls(issuerX509Certificate2); - RevocationResponse issuerRevocationResponse; - - if (issuerUrl.Count == 0) - { - // we need to validate with crl instead - // It does not contain the Authority Info Access, containng the rl to where the certificate must be validated - // We must therefore guess, that the certificate is valid. - CrlLookup crlLookupClient = new CrlLookup(); - issuerRevocationResponse = crlLookupClient.CheckCertificate(issuerX509Certificate2); - } - else - { - // hey, wow some url exist - lets use that - // don't thing this will ever happens anyway - issuerRevocationResponse = this.RevocationResponse(issuerX509Certificate2); - } - - // now to handle the issuerRevocationResponse - if (issuerRevocationResponse == null) - { - revocationResponse.IsValid = false; - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("The issuing certificate could not be validated."); - } - else - { - // the issuer certificate is validated, the validity of the issuer certificate - // is copied to the revocationResponse - revocationResponse.IsValid = issuerRevocationResponse.IsValid; - revocationResponse.Exception = issuerRevocationResponse.Exception; - } - - // update the cache - this.ocspCache.Set(x509Certificate2.Thumbprint.ToLowerInvariant(), revocationResponse); - } - } - else - { - // the certificate is NOT valid - // no need to check the issuer certificate - this.ocspCache.Set(x509Certificate2.Thumbprint.ToLowerInvariant(), revocationResponse); - } - } - else - { - // some exception returned. - // do not add to cache - } - } - - } - - return revocationResponse; - } - - public RevocationResponse RevocationResponseOnline(X509Certificate2 serverX509Certificate2, X509Certificate2 issuerX509Certificate2) - { - RevocationResponse revocationResponse = new RevocationResponse(); - - try - { - if (serverX509Certificate2 == null) - { - throw new Exception("Server certificate is null"); - } - - if (issuerX509Certificate2 == null) - { - throw new Exception("Issuer certificate for server certificate not identified"); - } - - // 0. Get server urls - List urlList = this.GetAuthorityInformationAccessOcspUrls(serverX509Certificate2); - - if (urlList.Count == 0) - { - throw new Exception("No OCSP url found in ee certificate."); - } - - // create BouncyCastle certificates - X509CertificateParser certParser = new X509CertificateParser(); - Org.BouncyCastle.X509.X509Certificate issuerX509Certificate = certParser.ReadCertificate(issuerX509Certificate2.RawData); - Org.BouncyCastle.X509.X509Certificate serverX509Certificate = certParser.ReadCertificate(serverX509Certificate2.RawData); - - // 1. Generate request - OcspReq req = this.GenerateOcspRequest(issuerX509Certificate, serverX509Certificate.SerialNumber); - OcspResp resp = null; - - foreach (var url in urlList) - { - // 2. make binary request online - resp = this.GetOnlineBinaryHttpResponse(req, url, "application/ocsp-request", "application/ocsp-response"); - - if (resp != null) - { - break; - } - } - - if (resp == null) - { - return null; - } - - //3. check result - revocationResponse = this.ProcessOcspResponse(serverX509Certificate, issuerX509Certificate, resp); - } - catch (CheckCertificateOcspUnexpectedException) - { - throw; - } - catch (ArgumentNullException e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("ArgumentNullException", e); - } - catch (OverflowException e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("OverflowException", e); - } - catch (FormatException e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("FormatException", e); - } - catch (CryptographicUnexpectedOperationException e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("CryptographicUnexpectedOperationException", e); - } - catch (CryptographicException e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("CryptographicException", e); - } - catch (Exception e) - { - revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("OCSP valideringen fejlede.", e); - } - - return revocationResponse; - } - - public X509Certificate2 FindIssuerCertificate(X509Certificate2 serverX509Certificate2) - { - X509Certificate2 issuerX509Certificate2 = null; - - // Find the issuer certificate - X509Chain x509Chain = new X509Chain(); - x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - x509Chain.Build(serverX509Certificate2); - - // Iterate though the chain, to validate if it contain a valid root vertificate - X509ChainElementCollection x509ChainElementCollection = x509Chain.ChainElements; - X509ChainElementEnumerator enumerator = x509ChainElementCollection.GetEnumerator(); - X509ChainElement x509ChainElement; - X509Certificate2 x509Certificate2 = null; - IDictionary map = new Dictionary(); - - // At this point, the certificate is not valid, until a - // it is proved that it has a valid root certificate - while (enumerator.MoveNext()) - { - x509ChainElement = enumerator.Current; - x509Certificate2 = x509ChainElement.Certificate; - map.Add(x509Certificate2.Subject, x509Certificate2); - } - - if (map.ContainsKey(serverX509Certificate2.Issuer)) - { - issuerX509Certificate2 = map[serverX509Certificate2.Issuer]; - } - - return issuerX509Certificate2; - } - - /* public X509Certificate2 FindRootCertificate(X509Certificate2 serverX509Certificate2, IDictionary rootCertificateDirectory) - { - bool rootCertificateFound = false; - X509Certificate2 desiredRootX509Certificate2 = null; - // Find the desired root certificate - X509Chain x509Chain = new X509Chain(); - x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - x509Chain.Build(serverX509Certificate2); - - // Iterate though the chain, to validate if it contain a valid root vertificate - X509ChainElementCollection x509ChainElementCollection = x509Chain.ChainElements; - X509ChainElementEnumerator enumerator = x509ChainElementCollection.GetEnumerator(); - X509ChainElement x509ChainElement; - X509Certificate2 x509Certificate2 = null; - string x509CertificateThumbprint; - // At this point, the certificate is not valid, until a - // it is proved that it has a valid root certificate - while (rootCertificateFound == false && enumerator.MoveNext()) - { - x509ChainElement = enumerator.Current; - x509Certificate2 = x509ChainElement.Certificate; - x509CertificateThumbprint = x509Certificate2.Thumbprint.ToLowerInvariant(); - if (rootCertificateDirectory.ContainsKey(x509CertificateThumbprint)) - { - // The current chain element is in the trusted rootCertificateDirectory - rootCertificateFound = true; - - // now the loop will break, as we have found a trusted root certificate - } - } - - if (rootCertificateFound) - { - // root certificate is found - desiredRootX509Certificate2 = x509Certificate2; - } - - return desiredRootX509Certificate2; - }*/ - - public List GetAuthorityInformationAccessOcspUrls(X509Certificate2 x509Certificate2) - { - List ocspUrls = new List(); - - try - { - // DanID test code shows how to do it - Org.BouncyCastle.Asn1.X509.X509Extensions x509Extensions = this.GetX509Extensions(x509Certificate2); - Org.BouncyCastle.Asn1.X509.X509Extension x509Extension = x509Extensions.GetExtension(Org.BouncyCastle.Asn1.X509.X509Extensions.AuthorityInfoAccess); - if (x509Extension == null) - { - // The desired info does not exist - // Meaning the certificate does not contain ocsp urls - } - else - { - Org.BouncyCastle.Asn1.X509.AuthorityInformationAccess authorityInformationAccess = Org.BouncyCastle.Asn1.X509.AuthorityInformationAccess.GetInstance(x509Extension.GetParsedValue()); - Org.BouncyCastle.Asn1.X509.AccessDescription[] accessDescription = authorityInformationAccess.GetAccessDescriptions(); - string ocspUrl = this.GetAccessDescriptionUrlForOid(AccessDescription.IdADOcsp, accessDescription); - ocspUrls.Add(ocspUrl); - } - } - catch (Exception e) - { - throw new Exception("Error parsing AIA.", e); - } - - return ocspUrls; - } - - //public List GetAuthorityInformationAccessOcspUrl(Org.BouncyCastle.X509.X509Certificate serverX509Certificate) - //{ - // List ocspUrls = new List(); - - // try - // { - // // "1.3.6.1.5.5.7.1.1" - // Asn1Object asn1Object = this.GetExtensionValue(rootX509Certificate, X509Extensions.AuthorityInfoAccess.Id); - - // if (asn1Object == null) - // { - // return null; - // } - // // For a strange reason I cannot acess the aia.AccessDescription[]. - // // Hope it will be fixed in the next version (1.5). - // // mySupply ApS - JLM - Still not working in 1.7 - // // AuthorityInformationAccess aia = AuthorityInformationAccess.GetInstance(asn1Object); - - // // Switched to manual parse - // Asn1Sequence s = (Asn1Sequence)asn1Object; - // IEnumerator elements = s.GetEnumerator(); - - // while (elements.MoveNext()) - // { - // Asn1Sequence element = (Asn1Sequence)elements.Current; - // DerObjectIdentifier oid = (DerObjectIdentifier)element[0]; - - // if (oid.Id.Equals("1.3.6.1.5.5.7.48.1")) // Is Ocsp - yes - // { - // Asn1TaggedObject taggedObject = (Asn1TaggedObject)element[1]; - // GeneralName gn = (GeneralName)GeneralName.GetInstance(taggedObject); - // ocspUrls.Add(((DerIA5String)DerIA5String.GetInstance(gn.Name)).GetString()); - // } - // } - // } - // catch (Exception e) - // { - // throw new Exception("Error parsing AIA.", e); - // } - - // return ocspUrls; - //} - - private X509Extensions GetX509Extensions(X509Certificate2 certificate) - { - try - { - var inputStream = new Asn1InputStream(certificate.RawData); - var certificateAsAsn1 = inputStream.ReadObject(); - var certificateStructure = X509CertificateStructure.GetInstance(certificateAsAsn1); - var toBeSignedPart = certificateStructure.TbsCertificate; - var extensions = toBeSignedPart.Extensions; - if (extensions == null) - { - throw new CheckCertificateOcspUnexpectedException("No X509 extensions found"); - } - return extensions; - } - catch (CheckCertificateOcspUnexpectedException) - { - throw; - } - catch (CertificateEncodingException e) - { - throw new ArgumentException("Error while extracting Access Description", e); - } - } - - private string GetAccessDescriptionUrlForOid(DerObjectIdentifier oid, AccessDescription[] authorityInformationAccessArray) - { - foreach (AccessDescription authorityInformationAcces in authorityInformationAccessArray) - { - if (oid.Equals(authorityInformationAcces.AccessMethod)) - { - var name = authorityInformationAcces.AccessLocation; - return ((DerIA5String)name.Name).GetString(); - } - } - return null; - } - - private Asn1Object GetExtensionValue(Org.BouncyCastle.X509.X509Certificate rootX509Certificate, string oid) - { - if (rootX509Certificate == null) - { - return null; - } - - byte[] bytes = rootX509Certificate.GetExtensionValue(new DerObjectIdentifier(oid)).GetOctets(); - - if (bytes == null) - { - return null; - } - - Asn1InputStream aIn = new Asn1InputStream(bytes); - - return aIn.ReadObject(); - } - - /* private OcspReq GenerateOcspRequest(X509Certificate2 rootX509Certificate2, BigInteger serialNumber) - { - X509Certificate rootX509Certificate = rootX509Certificate2.Export(X509ContentType.Cert); - return this.GenerateOcspRequest(rootX509Certificate, serialNumber); - }*/ - - /* private OcspReq GenerateOcspRequest(X509Certificate rootX509Certificate, byte serialNumber) - { - BigInteger serialNumberBigInteger = new BigInteger(serialNumber); - return this.GenerateOcspRequest(rootX509Certificate, serialNumberBigInteger); - }*/ - - - private OcspReq GenerateOcspRequest(Org.BouncyCastle.X509.X509Certificate rootX509Certificate, BigInteger serialNumber) - { - CertificateID certificateID = new CertificateID(CertificateID.HashSha1, rootX509Certificate, serialNumber); - return this.GenerateOcspRequest(certificateID); - } - - private OcspReq GenerateOcspRequest(CertificateID id) - { - OcspReqGenerator ocspRequestGenerator = new OcspReqGenerator(); - - ocspRequestGenerator.AddRequest(id); - - BigInteger nonce = BigInteger.ValueOf(new DateTime().Ticks); - - ArrayList oids = new ArrayList(); - Hashtable values = new Hashtable(); - - oids.Add(OcspObjectIdentifiers.PkixOcsp); - - Asn1OctetString asn1 = new DerOctetString(new DerOctetString(new byte[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1 })); - - values.Add(OcspObjectIdentifiers.PkixOcsp, new Org.BouncyCastle.Asn1.X509.X509Extension(false, asn1)); - X509Extensions x509Extensions = new X509Extensions(oids, values); - ocspRequestGenerator.SetRequestExtensions(x509Extensions); - - OcspReq ocspReq = ocspRequestGenerator.Generate(); - - return ocspRequestGenerator.Generate(); - } - - private OcspResp GetOnlineBinaryHttpResponse(OcspReq req, string url, string contentType, string accept) - { - byte[] data = req.GetEncoded(); - - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.Method = "POST"; - request.ContentType = contentType; - request.ContentLength = data.Length; - request.Accept = accept; - Stream stream = request.GetRequestStream(); - stream.Write(data, 0, data.Length); - stream.Close(); - HttpWebResponse response = (HttpWebResponse)request.GetResponse(); - Stream respStream = response.GetResponseStream(); - byte[] resp = this.ToByteArray(respStream); - respStream.Close(); - - return new OcspResp(resp); - } - - public byte[] ToByteArray(Stream stream) - { - byte[] buffer = new byte[this.BufferSize]; - MemoryStream ms = new MemoryStream(); - - int read = 0; - - while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) - { - ms.Write(buffer, 0, read); - } - - return ms.ToArray(); - } - - private RevocationResponse ProcessOcspResponse( - Org.BouncyCastle.X509.X509Certificate serverX509Certificate, - Org.BouncyCastle.X509.X509Certificate rootX509Certificate, - OcspResp ocspResponse) - { - //CertificateStatus cStatus = CertificateStatus.Unknown; - RevocationResponse revocationResponse = new RevocationResponse(); - - //X509CertificateParser parser = null; - //OcspRespStatus.Unauthorized - - switch (ocspResponse.Status) - { - case OcspRespStatus.Successful: - { - BasicOcspResp or = (BasicOcspResp)ocspResponse.GetResponseObject(); - - // ValidateResponse(or, issuerCert); - - string certificateSerial = Convert.ToUInt32(serverX509Certificate.SerialNumber.IntValue).ToString(); - bool found = false; - foreach (SingleResp singleResp in or.Responses) - { - if (singleResp.GetCertID().SerialNumber.ToString().Equals(certificateSerial)) - { - found = true; - - this.ValidateCertificateId(serverX509Certificate, rootX509Certificate, singleResp.GetCertID()); - - // ValidateThisUpdate(resp); - // ValidateNextUpdate(resp); - - Object certificateStatus = singleResp.GetCertStatus(); - - if (certificateStatus == null) - { - // acording to org.openoces.ooapi.utils.ocsp.ResponseParser.cs (DanID test code) - // in the TU11, method SerialNumberInResponseIsNotRevoked(..), - // when the certificateStatus is empty, all is okay - not revoked - // no revocation data exist - the certificate must be valid - revocationResponse.IsValid = true; - revocationResponse.NextUpdate = singleResp.NextUpdate.Value; - revocationResponse.RevocationCheckStatus = RevocationCheckStatus.AllChecksPassed; - } - else if (certificateStatus == Org.BouncyCastle.Ocsp.CertificateStatus.Good) - { - // this is the expected certificateStatus for valid certificates - // however if the status is good the certificateStatus is null - revocationResponse.IsValid = true; - revocationResponse.NextUpdate = singleResp.NextUpdate.Value; - revocationResponse.RevocationCheckStatus = RevocationCheckStatus.AllChecksPassed; - } - else if (certificateStatus is Org.BouncyCastle.Ocsp.RevokedStatus) - { - revocationResponse.IsValid = false; - revocationResponse.NextUpdate = singleResp.NextUpdate.Value; - revocationResponse.RevocationCheckStatus = RevocationCheckStatus.CertificateRevoked; - } - else if (certificateStatus is Org.BouncyCastle.Ocsp.UnknownStatus) - { - throw new CheckCertificateOcspUnexpectedException("CertificateStatus is Unknown"); - } - else - { - throw new CheckCertificateOcspUnexpectedException("CertificateStatus is unknown '" + certificateStatus.ToString() + "'."); - } - - // break foreach loop - break; - } - } - - if (!found) - { - // the returned result did not contain the desired certificate - throw new CheckCertificateOcspUnexpectedException("Revokation result did not contain the desired certificate serial number."); - } - - break; - } - default: - { - throw new CheckCertificateOcspUnexpectedException("Unknow status '" + ocspResponse.Status + "'."); - } - } - - return revocationResponse; - } - - //1. The certificate identified in a received response corresponds to - //that which was identified in the corresponding request; - private void ValidateCertificateId(Org.BouncyCastle.X509.X509Certificate serverX509Certificate, Org.BouncyCastle.X509.X509Certificate rootX509Certificate, CertificateID certificateId) - { - CertificateID expectedId = new CertificateID(CertificateID.HashSha1, rootX509Certificate, serverX509Certificate.SerialNumber); - - if (!expectedId.SerialNumber.Equals(certificateId.SerialNumber)) - { - throw new CheckCertificateOcspUnexpectedException("Invalid certificate ID in response"); - } - - if (!Org.BouncyCastle.Utilities.Arrays.AreEqual(expectedId.GetIssuerNameHash(), certificateId.GetIssuerNameHash())) - { - throw new CheckCertificateOcspUnexpectedException("Invalid certificate Issuer in response"); - } - } - - /// - /// Checks a certificate status on a ocsp server - /// - /// The certificate to check - /// The RevocationResponse object that contains the result - /// This exception is thrown, if an unexpected exception is thrown during the method - public RevocationResponse CheckCertificate(X509Certificate2 x509Certificate2) { - //To call the CheckCertificate asynchronously, we initialize the delegate and call it with IAsyncResult - RevocationResponse revocationResponse; - - bool ocspResponseExistsInCache = this.ocspCache.TryGetValue(x509Certificate2.Thumbprint.ToLowerInvariant(), out revocationResponse); - if (ocspResponseExistsInCache) - { - // response already in cache. - // Check if the response still is valid - if (revocationResponse.NextUpdate < DateTime.Now) - { - // the cached value is to old - // get new value - revocationResponse = this.CheckCertificateAsync(x509Certificate2); - } - } - else - { - // respons is not in cache - revocationResponse = this.CheckCertificateAsync(x509Certificate2); - } - - return revocationResponse; - } - - /// - /// Checks a certificate status on a ocsp server - /// - /// The certificate to check - /// The RevocationResponse object that contains the result - /// This exception is thrown, if an unexpected exception is thrown during the method - public RevocationResponse CheckCertificateAsync(X509Certificate2 certificate) - { - //To call the CheckCertificate asynchronously, we initialize the delegate and call it with IAsyncResult - RevocationResponse response; - - AsyncOcspCall asyncOcspCall = new AsyncOcspCall(this.RevocationResponse); - IAsyncResult asyncResult = asyncOcspCall.BeginInvoke(certificate, null, null); - - bool ocspRepliedInTime = asyncResult.AsyncWaitHandle.WaitOne(Utilities.TimeSpanInMilliseconds(TimeSpan.FromMilliseconds(_configuration.DefaultTimeoutMsec)), false); - if (ocspRepliedInTime) - { - // okay, the operation has finish. - response = asyncOcspCall.EndInvoke(asyncResult); - } - else - { - // Note - The validation is still running, and is not closed - - // operation timeout - throw new CertificateRevokedTimeoutException(TimeSpan.FromMilliseconds(_configuration.DefaultTimeoutMsec)); - } - - return response; - } - } +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this + * file except in compliance with the License. You may obtain + * a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * + * The Original Code is .NET RASP toolkit. + * + * The Initial Developer of the Original Code is Accenture and Avanade. + * Portions created by Accenture and Avanade are Copyright (C) 2009 + * Danish National IT and Telecom Agency (http://www.itst.dk). + * All Rights Reserved. + * + * Contributor(s): + * Gert Sylvest, Avanade + * Jesper Jensen, Avanade + * Ramzi Fadel, Avanade + * Patrik Johansson, Accenture + * Dennis Søgaard, Accenture + * Christian Pedersen, Accenture + * Martin Bentzen, Accenture + * Mikkel Hippe Brun, ITST + * Finn Hartmann Jordal, ITST + * Christian Lanng, ITST + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text.RegularExpressions; +using dk.gov.oiosi.common; +using dk.gov.oiosi.common.cache; +using dk.gov.oiosi.configuration; +using Org.BouncyCastle.Ocsp; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; +using dk.gov.oiosi.security.revocation.crl; +using Org.BouncyCastle.Security.Certificates; +using dk.gov.oiosi.logging; +using System.Linq; +//using Novell.Directory.Ldap.Asn1; + +namespace dk.gov.oiosi.security.revocation.ocsp +{ + + /// + /// Class for checking certificate revocation status against an OCSP (Online Certificate Status Protocol) server. + /// + public class OcspLookup : IRevocationLookup + { + private ILogger logger; + private OcspConfig _configuration; + + private readonly int BufferSize = 4096 * 8; + + /// + /// List of the default OCES (OCES1 and OCES2) root certificate + /// + //private IList defaultOcesRootCertificateList; + + private IDictionary rootCertificateDirectory; + + private ICache ocspCache; + + /// + /// Instantiates OcspLookup and loads the OCES default root certificate + /// + /// Configuration parameters + public OcspLookup(OcspConfig configuration) + { + this.logger = LoggerFactory.Create(this); + this.Init(configuration, null); + } + + /// + /// Constructor + /// + /// Configuration + /// The default root certificate + public OcspLookup(OcspConfig configuration, IList defaultRootCertificateList) + { + this.logger = LoggerFactory.Create(this); + this.Init(configuration, defaultRootCertificateList); + } + + /// + /// Default constructor. Attempts to load configuration from configuration file. + /// + public OcspLookup() + { + this.logger = LoggerFactory.Create(this); + OcspConfig configuration = ConfigurationHandler.GetConfigurationSection(); + this.Init(configuration, null); + } + + + /// + /// Gets the configuration of the lookup client + /// + public OcspConfig Configuration + { + get { return _configuration; } + } + + /// + /// To be able to call the CheckCertificate method asynchron, we create a delegate. + /// + /// the certificate to check + /// the OcSpResponse object to store the result + public delegate RevocationResponse AsyncOcspCall(X509Certificate2 certificate); + + /// + /// Initializes. If the default root certificate is set to null, an attempt is made + /// to get a default root certificate from a Configuration.xml file + /// + /// OCSP configuration + /// If the default root certificate is set to null, an attempt is made + /// to get a default root certificate from a Configuration.xml file + private void Init(OcspConfig configuration, IList defaultRootCertificateList) + { + this.ocspCache = CacheFactory.Instance.OcspLookupCache; + try + { + // 1. Set configuration + _configuration = configuration; + + // 2. Get default certificate: + if (defaultRootCertificateList == null) + { + defaultRootCertificateList = _configuration.GetDefaultOcesRootCertificateListFromStore(); + } + + // put the root certificates into the directory + + this.rootCertificateDirectory = new Dictionary(); + + foreach (X509Certificate2 x509Certificate2 in defaultRootCertificateList) + { + this.rootCertificateDirectory.Add(x509Certificate2.SerialNumber.ToLowerInvariant(), x509Certificate2); + } + } + catch (UriFormatException) + { + throw; + } + catch (ArgumentNullException) + { + throw; + } + catch (OverflowException) + { + throw; + } + catch (FormatException) + { + throw; + } + catch (CryptographicUnexpectedOperationException) + { + throw; + } + catch (CryptographicException) + { + throw; + } + catch (Exception) + { + throw; + } + } + + /// + /// Checks a certificate status on a ocsp server + /// + /// The certificate to check + /// The RevocationResponse object that contains the result + /// This exception is thrown, if an unexpected exception is thrown during the method + public RevocationResponse RevocationResponse(X509Certificate2 x509Certificate2) + { + // this method can be call requsiv, so check the cache first + RevocationResponse revocationResponse; + + this.logger.Debug(string.Format("OCSP validation the certificate '{0}'.", x509Certificate2.SubjectName.Name)); + + bool ocspResponseExistsInCache = this.ocspCache.TryGetValue(x509Certificate2.SerialNumber.ToLowerInvariant(), out revocationResponse); + if (ocspResponseExistsInCache) + { + // response already in cache. + // Check if the response still is valid + if (revocationResponse.NextUpdate < DateTime.Now) + { + // the cached value is to old + // get new value + revocationResponse = this.RevocationResponseOnline(x509Certificate2); + } + } + else + { + // respons is not in cache + revocationResponse = this.RevocationResponseOnline(x509Certificate2); + } + + if (this.logger.IsInfoEnabled) + { + if (!revocationResponse.IsValid) + { + this.logger.Info(string.Format("Certificate '{0}' is revoked.", x509Certificate2.SubjectName.Name)); + } + } + + return revocationResponse; + } + + /// + /// Checks a certificate status on a ocsp server + /// + /// The certificate to check + /// The RevocationResponse object that contains the result + /// This exception is thrown, if an unexpected exception is thrown during the method + public RevocationResponse RevocationResponseOnline(X509Certificate2 x509Certificate2) + { + RevocationResponse revocationResponse = new RevocationResponse(); + + if (x509Certificate2 == null) + { + throw new CheckCertificateOcspUnexpectedException(); + } + // http://bouncy-castle.1462172.n4.nabble.com/c-ocsp-verification-td3160243.html + X509Certificate2 issuerX509Certificate2 = new CertificateUtil().FindIssuerCertificate(x509Certificate2); + + if (issuerX509Certificate2 == null) + { + throw new CheckCertificateOcspUnexpectedException("Issuer certificate '" + x509Certificate2.Issuer + "' not found."); + } + + if (issuerX509Certificate2.SerialNumber.Equals(x509Certificate2.SerialNumber, StringComparison.InvariantCultureIgnoreCase)) + { + // the certificate and the issuer certificace is the same + // this mean that the root certificate is not trusted + throw new CheckCertificateOcspUnexpectedException("E-RSP19442: Certificate not trusted, as the certificate is self-signed"); + } + else + { + revocationResponse = this.RevocationResponseOnline(x509Certificate2, issuerX509Certificate2); + + if (revocationResponse != null && revocationResponse.IsValid) + { + if (revocationResponse.Exception == null) + { + // no exception recorded + + // now we know the certificate is valid. + // if the issuer is a trusted root certificate, all is good + if (this.rootCertificateDirectory.ContainsKey(issuerX509Certificate2.SerialNumber.ToLowerInvariant())) + { + // the root certificate is trusted, so the RevocationResponse can be put on the cache + this.ocspCache.Set(x509Certificate2.SerialNumber.ToLowerInvariant(), revocationResponse); + } + else + { + // we do not yet know if the certificate is valid. + // the certificate migth be good, but if the issueing certificate is revoked, + // then the certificate should also be revoked. + // Validate the issuer certificate + // this is required, because certificate can have a chain that is longer then 2 + + // The only problem is, that we can not ocsp validate the intermiddel certificate (the issuer certificate). + // acording to DanID - that certificate can only be validated with CRL + // Note : The crl list will be/should be very short. Only containing the issuer certificate that has been revoked. + // A good guess is that there at all time will be most 10 issuer certificate, so the list of revoked issuer certificate is short. + IList issuerUrl = this.GetAuthorityInformationAccessOcspUrls(issuerX509Certificate2); + RevocationResponse issuerRevocationResponse; + + if (issuerUrl.Count == 0) + { + // we need to validate with crl instead + // It does not contain the Authority Info Access, containng the rl to where the certificate must be validated + // We must therefore guess, that the certificate is valid. + CrlLookup crlLookupClient = new CrlLookup(); + issuerRevocationResponse = crlLookupClient.CheckCertificate(issuerX509Certificate2); + } + else + { + // hey, wow some url exist - lets use that + // don't thing this will ever happens anyway + issuerRevocationResponse = this.RevocationResponse(issuerX509Certificate2); + } + + // now to handle the issuerRevocationResponse + if (issuerRevocationResponse == null) + { + revocationResponse.IsValid = false; + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("The issuing certificate could not be validated."); + } + else + { + // the issuer certificate is validated, the validity of the issuer certificate + // is copied to the revocationResponse + revocationResponse.IsValid = issuerRevocationResponse.IsValid; + revocationResponse.Exception = issuerRevocationResponse.Exception; + } + + // update the cache + this.ocspCache.Set(x509Certificate2.SerialNumber.ToLowerInvariant(), revocationResponse); + } + + } + else + { + + // some exception returned. + // do not add to cache + } + } + else + { + // the certificate is Not valid + // no need to check the issuer certificate + this.ocspCache.Set(x509Certificate2.SerialNumber.ToLowerInvariant(), revocationResponse); + } + + + } + + return revocationResponse; + } + + public RevocationResponse RevocationResponseOnline(X509Certificate2 serverX509Certificate2, X509Certificate2 issuerX509Certificate2) + { + RevocationResponse revocationResponse = new RevocationResponse(); + + try + { + if (serverX509Certificate2 == null) + { + throw new Exception("Server certificate is null"); + } + + if (issuerX509Certificate2 == null) + { + throw new Exception("Issuer certificate for server certificate not identified"); + } + + // 0. Get server urls + List urlList = this.GetAuthorityInformationAccessOcspUrls(serverX509Certificate2); + + if (urlList.Count == 0) + { + throw new Exception("No OCSP url found in ee certificate."); + } + + // create BouncyCastle certificates + X509CertificateParser certParser = new X509CertificateParser(); + Org.BouncyCastle.X509.X509Certificate issuerX509Certificate = certParser.ReadCertificate(issuerX509Certificate2.RawData); + Org.BouncyCastle.X509.X509Certificate serverX509Certificate = certParser.ReadCertificate(serverX509Certificate2.RawData); + + // 1. Generate request + OcspReq req = this.GenerateOcspRequest(issuerX509Certificate, serverX509Certificate.SerialNumber); + OcspResp resp = null; + + foreach (var url in urlList) + { + // 2. make binary request online + resp = this.GetOnlineBinaryHttpResponse(req, url, "application/ocsp-request", "application/ocsp-response"); + + if (resp != null) + { + break; + } + } + + if (resp == null) + { + return null; + } + + //3. check result + revocationResponse = this.ProcessOcspResponse(serverX509Certificate, issuerX509Certificate, resp); + } + catch (CheckCertificateOcspUnexpectedException) + { + throw; + } + catch (ArgumentNullException e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("ArgumentNullException", e); + } + catch (OverflowException e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("OverflowException", e); + } + catch (FormatException e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("FormatException", e); + } + catch (CryptographicUnexpectedOperationException e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("CryptographicUnexpectedOperationException", e); + } + catch (CryptographicException e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("CryptographicException", e); + } + catch (Exception e) + { + revocationResponse.Exception = new CheckCertificateOcspUnexpectedException("OCSP valideringen fejlede.", e); + } + + return revocationResponse; + } + + + + public List GetAuthorityInformationAccessOcspUrls(X509Certificate2 x509Certificate2) + { + List ocspUrls = new List(); + + try + { + // DanID test code shows how to do it + Org.BouncyCastle.Asn1.X509.X509Extensions x509Extensions = this.GetX509Extensions(x509Certificate2); + Org.BouncyCastle.Asn1.X509.X509Extension x509Extension = x509Extensions.GetExtension(Org.BouncyCastle.Asn1.X509.X509Extensions.AuthorityInfoAccess); + if (x509Extension == null) + { + // The desired info does not exist + // Meaning the certificate does not contain ocsp urls + } + else + { + Org.BouncyCastle.Asn1.X509.AuthorityInformationAccess authorityInformationAccess = Org.BouncyCastle.Asn1.X509.AuthorityInformationAccess.GetInstance(x509Extension.GetParsedValue()); + Org.BouncyCastle.Asn1.X509.AccessDescription[] accessDescription = authorityInformationAccess.GetAccessDescriptions(); + string ocspUrl = this.GetAccessDescriptionUrlForOid(AccessDescription.IdADOcsp, accessDescription); + ocspUrls.Add(ocspUrl); + } + } + catch (Exception e) + { + throw new Exception("Error parsing AIA.", e); + } + + return ocspUrls; + } + + //public List GetAuthorityInformationAccessOcspUrl(Org.BouncyCastle.X509.X509Certificate serverX509Certificate) + //{ + // List ocspUrls = new List(); + + // try + // { + // // "1.3.6.1.5.5.7.1.1" + // Asn1Object asn1Object = this.GetExtensionValue(rootX509Certificate, X509Extensions.AuthorityInfoAccess.Id); + + // if (asn1Object == null) + // { + // return null; + // } + // // For a strange reason I cannot acess the aia.AccessDescription[]. + // // Hope it will be fixed in the next version (1.5). + // // mySupply ApS - JLM - Still not working in 1.7 + // // AuthorityInformationAccess aia = AuthorityInformationAccess.GetInstance(asn1Object); + + // // Switched to manual parse + // Asn1Sequence s = (Asn1Sequence)asn1Object; + // IEnumerator elements = s.GetEnumerator(); + + // while (elements.MoveNext()) + // { + // Asn1Sequence element = (Asn1Sequence)elements.Current; + // DerObjectIdentifier oid = (DerObjectIdentifier)element[0]; + + // if (oid.Id.Equals("1.3.6.1.5.5.7.48.1")) // Is Ocsp - yes + // { + // Asn1TaggedObject taggedObject = (Asn1TaggedObject)element[1]; + // GeneralName gn = (GeneralName)GeneralName.GetInstance(taggedObject); + // ocspUrls.Add(((DerIA5String)DerIA5String.GetInstance(gn.Name)).GetString()); + // } + // } + // } + // catch (Exception e) + // { + // throw new Exception("Error parsing AIA.", e); + // } + + // return ocspUrls; + //} + + private X509Extensions GetX509Extensions(X509Certificate2 certificate) + { + try + { + var inputStream = new Asn1InputStream(certificate.RawData); + var certificateAsAsn1 = inputStream.ReadObject(); + var certificateStructure = X509CertificateStructure.GetInstance(certificateAsAsn1); + var toBeSignedPart = certificateStructure.TbsCertificate; + var extensions = toBeSignedPart.Extensions; + if (extensions == null) + { + throw new CheckCertificateOcspUnexpectedException("No X509 extensions found"); + } + return extensions; + } + catch (CheckCertificateOcspUnexpectedException) + { + throw; + } + catch (CertificateEncodingException e) + { + throw new ArgumentException("Error while extracting Access Description", e); + } + } + + private string GetAccessDescriptionUrlForOid(DerObjectIdentifier oid, AccessDescription[] authorityInformationAccessArray) + { + foreach (AccessDescription authorityInformationAcces in authorityInformationAccessArray) + { + if (oid.Equals(authorityInformationAcces.AccessMethod)) + { + var name = authorityInformationAcces.AccessLocation; + return ((DerIA5String)name.Name).GetString(); + } + } + return null; + } + + private Asn1Object GetExtensionValue(Org.BouncyCastle.X509.X509Certificate rootX509Certificate, string oid) + { + if (rootX509Certificate == null) + { + return null; + } + + byte[] bytes = rootX509Certificate.GetExtensionValue(new DerObjectIdentifier(oid)).GetOctets(); + + if (bytes == null) + { + return null; + } + + Asn1InputStream aIn = new Asn1InputStream(bytes); + + return aIn.ReadObject(); + } + + /* private OcspReq GenerateOcspRequest(X509Certificate2 rootX509Certificate2, BigInteger serialNumber) + { + X509Certificate rootX509Certificate = rootX509Certificate2.Export(X509ContentType.Cert); + return this.GenerateOcspRequest(rootX509Certificate, serialNumber); + }*/ + + /* private OcspReq GenerateOcspRequest(X509Certificate rootX509Certificate, byte serialNumber) + { + BigInteger serialNumberBigInteger = new BigInteger(serialNumber); + return this.GenerateOcspRequest(rootX509Certificate, serialNumberBigInteger); + }*/ + + + private OcspReq GenerateOcspRequest(Org.BouncyCastle.X509.X509Certificate rootX509Certificate, BigInteger serialNumber) + { + CertificateID certificateID = new CertificateID(CertificateID.HashSha1, rootX509Certificate, serialNumber); + return this.GenerateOcspRequest(certificateID); + } + + private OcspReq GenerateOcspRequest(CertificateID id) + { + OcspReqGenerator ocspRequestGenerator = new OcspReqGenerator(); + + ocspRequestGenerator.AddRequest(id); + + BigInteger nonce = BigInteger.ValueOf(new DateTime().Ticks); + + ArrayList oids = new ArrayList(); + Hashtable values = new Hashtable(); + + oids.Add(OcspObjectIdentifiers.PkixOcsp); + + Asn1OctetString asn1 = new DerOctetString(new DerOctetString(new byte[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1 })); + + values.Add(OcspObjectIdentifiers.PkixOcsp, new Org.BouncyCastle.Asn1.X509.X509Extension(false, asn1)); + X509Extensions x509Extensions = new X509Extensions(oids, values); + ocspRequestGenerator.SetRequestExtensions(x509Extensions); + + OcspReq ocspReq = ocspRequestGenerator.Generate(); + + return ocspRequestGenerator.Generate(); + } + + private OcspResp GetOnlineBinaryHttpResponse(OcspReq req, string url, string contentType, string accept) + { + byte[] data = req.GetEncoded(); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = contentType; + request.ContentLength = data.Length; + request.Accept = accept; + Stream stream = request.GetRequestStream(); + stream.Write(data, 0, data.Length); + stream.Close(); + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + Stream respStream = response.GetResponseStream(); + byte[] resp = this.ToByteArray(respStream); + respStream.Close(); + + return new OcspResp(resp); + } + + public byte[] ToByteArray(Stream stream) + { + byte[] buffer = new byte[this.BufferSize]; + MemoryStream ms = new MemoryStream(); + + int read = 0; + + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + + return ms.ToArray(); + } + + private RevocationResponse ProcessOcspResponse( + Org.BouncyCastle.X509.X509Certificate serverX509Certificate, + Org.BouncyCastle.X509.X509Certificate rootX509Certificate, + OcspResp ocspResponse) + { + //CertificateStatus cStatus = CertificateStatus.Unknown; + RevocationResponse revocationResponse = new RevocationResponse(); + + //X509CertificateParser parser = null; + //OcspRespStatus.Unauthorized + + switch (ocspResponse.Status) + { + case OcspRespStatus.Successful: + { + BasicOcspResp or = (BasicOcspResp)ocspResponse.GetResponseObject(); + + // ValidateResponse(or, issuerCert); + + string certificateSerial = serverX509Certificate.SerialNumber.ToString(); + bool found = false; + foreach (SingleResp singleResp in or.Responses) + { + if (singleResp.GetCertID().SerialNumber.ToString().Equals(certificateSerial)) + { + found = true; + + this.ValidateCertificateId(serverX509Certificate, rootX509Certificate, singleResp.GetCertID()); + + // ValidateThisUpdate(resp); + // ValidateNextUpdate(resp); + + Object certificateStatus = singleResp.GetCertStatus(); + + if (certificateStatus == null) + { + // acording to org.openoces.ooapi.utils.ocsp.ResponseParser.cs (DanID test code) + // in the TU11, method SerialNumberInResponseIsNotRevoked(..), + // when the certificateStatus is empty, all is okay - not revoked + // no revocation data exist - the certificate must be valid + revocationResponse.IsValid = true; + revocationResponse.NextUpdate = singleResp.NextUpdate.Value; + revocationResponse.RevocationCheckStatus = RevocationCheckStatus.AllChecksPassed; + } + else if (certificateStatus == Org.BouncyCastle.Ocsp.CertificateStatus.Good) + { + // this is the expected certificateStatus for valid certificates + // however if the status is good the certificateStatus is null + revocationResponse.IsValid = true; + revocationResponse.NextUpdate = singleResp.NextUpdate.Value; + revocationResponse.RevocationCheckStatus = RevocationCheckStatus.AllChecksPassed; + } + else if (certificateStatus is Org.BouncyCastle.Ocsp.RevokedStatus) + { + revocationResponse.IsValid = false; + revocationResponse.NextUpdate = singleResp.NextUpdate.Value; + revocationResponse.RevocationCheckStatus = RevocationCheckStatus.CertificateRevoked; + } + else if (certificateStatus is Org.BouncyCastle.Ocsp.UnknownStatus) + { + throw new CheckCertificateOcspUnexpectedException("CertificateStatus is Unknown"); + } + else + { + throw new CheckCertificateOcspUnexpectedException("CertificateStatus is unknown '" + certificateStatus.ToString() + "'."); + } + + // break foreach loop + break; + } + } + + if (!found) + { + // the returned result did not contain the desired certificate + throw new CheckCertificateOcspUnexpectedException("Revokation result did not contain the desired certificate serial number."); + } + + break; + } + default: + { + throw new CheckCertificateOcspUnexpectedException("Unknow status '" + ocspResponse.Status + "'."); + } + } + + return revocationResponse; + } + + //1. The certificate identified in a received response corresponds to + //that which was identified in the corresponding request; + private void ValidateCertificateId(Org.BouncyCastle.X509.X509Certificate serverX509Certificate, Org.BouncyCastle.X509.X509Certificate rootX509Certificate, CertificateID certificateId) + { + CertificateID expectedId = new CertificateID(CertificateID.HashSha1, rootX509Certificate, serverX509Certificate.SerialNumber); + + if (!expectedId.SerialNumber.Equals(certificateId.SerialNumber)) + { + throw new CheckCertificateOcspUnexpectedException("Invalid certificate ID in response"); + } + + if (!Org.BouncyCastle.Utilities.Arrays.AreEqual(expectedId.GetIssuerNameHash(), certificateId.GetIssuerNameHash())) + { + throw new CheckCertificateOcspUnexpectedException("Invalid certificate Issuer in response"); + } + } + + /// + /// Checks a certificate status on a ocsp server + /// + /// The certificate to check + /// The RevocationResponse object that contains the result + /// This exception is thrown, if an unexpected exception is thrown during the method + public RevocationResponse CheckCertificate(X509Certificate2 x509Certificate2) + { + //To call the CheckCertificate asynchronously, we initialize the delegate and call it with IAsyncResult + RevocationResponse revocationResponse; + + bool ocspResponseExistsInCache = this.ocspCache.TryGetValue(x509Certificate2.SerialNumber.ToLowerInvariant(), out revocationResponse); + if (ocspResponseExistsInCache) + { + // response already in cache. + // Check if the response still is valid + + + if (revocationResponse.NextUpdate < DateTime.Now) + { + // the cached value is to old + // get new value + revocationResponse = this.CheckCertificateAsync(x509Certificate2); + } + } + else + { + // respons is not in cache + revocationResponse = this.CheckCertificateAsync(x509Certificate2); + } + + return revocationResponse; + } + + /// + /// Checks a certificate status on a ocsp server + /// + /// The certificate to check + /// The RevocationResponse object that contains the result + /// This exception is thrown, if an unexpected exception is thrown during the method + public RevocationResponse CheckCertificateAsync(X509Certificate2 certificate) + { + //To call the CheckCertificate asynchronously, we initialize the delegate and call it with IAsyncResult + RevocationResponse response; + + AsyncOcspCall asyncOcspCall = new AsyncOcspCall(this.RevocationResponse); + IAsyncResult asyncResult = asyncOcspCall.BeginInvoke(certificate, null, null); + + bool ocspRepliedInTime = asyncResult.AsyncWaitHandle.WaitOne(_configuration.DefaultTimeoutMsec, false); + if (ocspRepliedInTime) + { + // okay, the operation has finish. + response = asyncOcspCall.EndInvoke(asyncResult); + } + else + { + // Note - The validation is still running, and is not closed + + // operation timeout + throw new CertificateRevokedTimeoutException(TimeSpan.FromMilliseconds(_configuration.DefaultTimeoutMsec)); + } + + return response; + } + } } \ No newline at end of file diff --git a/src/dk.gov.oiosi/security/validation/CertificateValidator.cs b/src/dk.gov.oiosi/security/validation/CertificateValidator.cs index de54f23b6f8c24cd3ca6030b48ab9bda05c27d37..6186ed93ce0a09420839060e49d6bd5fb4a6255b 100644 --- a/src/dk.gov.oiosi/security/validation/CertificateValidator.cs +++ b/src/dk.gov.oiosi/security/validation/CertificateValidator.cs @@ -132,13 +132,13 @@ namespace dk.gov.oiosi.security.validation // Check if the certificate has the default root certificate as its root bool rootIsInChain = false; - string rootThumbprint = rootCertificate.Thumbprint.ToLower(); + string rootSerialNumber = rootCertificate.SerialNumber.ToLower(); - if (certificate.Thumbprint.ToLower() != rootThumbprint) + if (!certificate.SerialNumber.Equals(rootSerialNumber, StringComparison.InvariantCultureIgnoreCase)) { foreach (X509ChainElement chainElem in chain.ChainElements) { - if (chainElem.Certificate.Thumbprint.ToLower() == rootThumbprint) + if (chainElem.Certificate.SerialNumber.Equals(rootSerialNumber, StringComparison.InvariantCultureIgnoreCase)) { rootIsInChain = true; break; diff --git a/test/dk.gov.oiosi.test.unit/TestConstants.cs b/test/dk.gov.oiosi.test.unit/TestConstants.cs index 51431ec48c9cc95f5b5d939b5b932be0f49481ab..57e96470673393bfc6c6bdd51ee54ffbb88599f2 100644 --- a/test/dk.gov.oiosi.test.unit/TestConstants.cs +++ b/test/dk.gov.oiosi.test.unit/TestConstants.cs @@ -100,11 +100,13 @@ namespace dk.gov.oiosi.test.unit public const string PATH_CERTIFICATE_DEVICE = "Resources/Certificates/CVR30808460.Expire20200130.TU GENEREL FOCES gyldig (Funktionscertifikat).pfx"; public const string PASSWORD_CERTIFICATE_DEVICE = "Test1234"; + public const string PATH_CERTIFICATE_MITID_DEVICE = "Resources/Certificates/Nemhandel-DEV-OCES-cert-20210422.pfx"; ////public const string PATH_CERTIFICATE_ROOT1 = "Resources/Certificates/TDC OCES Systemtest CA II.cer"; ////public const string PATH_CERTIFICATE_ROOT2 = "Resources/Certificates/TRUST2408 Systemtest VII Primary CA.cer"; public const string PATH_CERTIFICATE_TEST_ROOT_OCES1 = "Resources/Certificates/TDC OCES Systemtest CA II.cer"; public const string PATH_CERTIFICATE_TEST_ROOT_OCES2 = "Resources/Certificates/TRUST2408 Systemtest VII Primary CA.cer"; + public const string PATH_CERTIFICATE_TEST_ROOT_MITID = "Resources/Certificates/MitID_root_CA.cer"; public const string PATH_CERTIFICATE_PROD_ROOT_OCES1 = "Resources/Certificates/TDC OCES CA.cer"; public const string PATH_CERTIFICATE_PROD_ROOT_OCES2 = "Resources/Certificates/TRUST2408 OCES Primary CA.cer"; diff --git a/test/dk.gov.oiosi.test.unit/dk.gov.oiosi.test.unit.csproj b/test/dk.gov.oiosi.test.unit/dk.gov.oiosi.test.unit.csproj index e093d5028700afb19912d964e2baaa2381a8d19a..924f97a27e328f40198ec413a2768fccd1f0e46e 100644 --- a/test/dk.gov.oiosi.test.unit/dk.gov.oiosi.test.unit.csproj +++ b/test/dk.gov.oiosi.test.unit/dk.gov.oiosi.test.unit.csproj @@ -50,6 +50,9 @@ ..\..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll + + ..\..\packages\Saxon-HE.10.3.0\lib\net40\saxon-he-api-10.3.dll + @@ -151,11 +154,23 @@ Resources\Certificates\CVR30808460.Expire20131101.Test MOCES1 %28medarbejdercertificat 1%29.pfx PreserveNewest + + Resources\Certificates\Nemhandel-DEV-OCES-cert-20210422.p12 + PreserveNewest + + + Resources\Certificates\Nemhandel-DEV-OCES-cert-20210422.pfx + PreserveNewest + + + Resources\Certificates\MitID_root_CA.cer + PreserveNewest + Resources\Certificates\TDC OCES Systemtest CA II.cer PreserveNewest - + Resources\Certificates\TRUST2408 Systemtest VII Primary CA.cer PreserveNewest @@ -241,8 +256,8 @@ Resources\Documents\Examples\OIOUBL_Invoice_identifier_ean_5798009811561.xml PreserveNewest - - Resources\Documents\Examples\OIOUBL_Invoice_identifier_ean_v2p1.xml + + Resources\Documents\Examples\OIOUBL_Invoice_identifier_gln_v2p1.xml PreserveNewest @@ -680,6 +695,9 @@ + + + @@ -688,11 +706,11 @@ - \ No newline at end of file diff --git a/test/dk.gov.oiosi.test.unit/packages.config b/test/dk.gov.oiosi.test.unit/packages.config index 208e09ad0787d8bc2b3c364b058b32ce6475af52..75172e852033dc5973fba60b30f6b9fe8b60f0dc 100644 --- a/test/dk.gov.oiosi.test.unit/packages.config +++ b/test/dk.gov.oiosi.test.unit/packages.config @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/test/dk.gov.oiosi.test.unit/raspProfile/communication/RaspRequestTest.cs b/test/dk.gov.oiosi.test.unit/raspProfile/communication/RaspRequestTest.cs index 730bd92b74c2c73caaf6518ff3376090cab98170..9700120d930f5fbaf40a5595cb2a55b106d729a3 100644 --- a/test/dk.gov.oiosi.test.unit/raspProfile/communication/RaspRequestTest.cs +++ b/test/dk.gov.oiosi.test.unit/raspProfile/communication/RaspRequestTest.cs @@ -58,7 +58,7 @@ namespace dk.gov.oiosi.test.unit.raspProfile.communication { } private OiosiMessage GetInvoiceOiosiMessage() { - var invoiceSourcePath = "Resources/Documents/Examples/OIOUBL_Invoice_identifier_ean_v2p1.xml"; + var invoiceSourcePath = "Resources/Documents/Examples/OIOUBL_Invoice_identifier_gln_v2p1.xml"; var invoiceFile = Settings.CreateRandomPath("invoice.xml"); Directory.CreateDirectory(invoiceFile.DirectoryName); File.Copy(invoiceSourcePath, invoiceFile.FullName); diff --git a/test/dk.gov.oiosi.test.unit/security/revocation/LookupTest.cs b/test/dk.gov.oiosi.test.unit/security/revocation/LookupTest.cs index 0cb397d01ef13b0dd1f41c3fa462e64d737c5da1..972a645be9d40b5a6677813884657d07876a30ae 100644 --- a/test/dk.gov.oiosi.test.unit/security/revocation/LookupTest.cs +++ b/test/dk.gov.oiosi.test.unit/security/revocation/LookupTest.cs @@ -12,8 +12,11 @@ namespace dk.gov.oiosi.test.unit.security.revocation public const string foces2RevokedCertificate = "Resources/Certificates/CVR30808460.Expire20200313.TU GENEREL FOCES spaerret (Funktionscertifikat).pfx"; public const string foces2OkayCertificate = TestConstants.PATH_CERTIFICATE_DEVICE;//"Resources/Certificates/CVR30808460.Expire20200130.TU GENEREL FOCES gyldig (Funktionscertifikat).pfx"; + + public const string mitIdFocesOkayCertificate = TestConstants.PATH_CERTIFICATE_MITID_DEVICE;//"Resources/Certificates/CVR30808460.Expire20200130.TU GENEREL FOCES gyldig (Funktionscertifikat).pfx"; public const string oces1RootCertificate = TestConstants.PATH_CERTIFICATE_TEST_ROOT_OCES1; public const string oces2RootCertificate = TestConstants.PATH_CERTIFICATE_TEST_ROOT_OCES2; + public const string mitIdRootCertificate = TestConstants.PATH_CERTIFICATE_TEST_ROOT_MITID; } } \ No newline at end of file diff --git a/test/dk.gov.oiosi.test.unit/security/revocation/OcspLookupTest.cs b/test/dk.gov.oiosi.test.unit/security/revocation/OcspLookupTest.cs index 7b43b36f26101f53073477e434ace53e953ddb6e..e59668e583452fce7a494d78fa43e7f6c4c97631 100644 --- a/test/dk.gov.oiosi.test.unit/security/revocation/OcspLookupTest.cs +++ b/test/dk.gov.oiosi.test.unit/security/revocation/OcspLookupTest.cs @@ -4,6 +4,11 @@ using NUnit.Framework; using System.Security.Cryptography.X509Certificates; using System.Collections.Generic; using System; +using System.Linq; +using Org.BouncyCastle.Pkcs; +using System.IO; +using dk.gov.oiosi.security.revocation.crl; +using System.Security.Cryptography; namespace dk.gov.oiosi.test.unit.security.revocation { @@ -22,12 +27,14 @@ namespace dk.gov.oiosi.test.unit.security.revocation { OcspConfig ocspConfig = new OcspConfig(); ocspConfig.DefaultTimeoutMsec = 20000; - + X509Certificate2 oces2RootCertificate = new X509Certificate2(LookupTest.oces2RootCertificate); + X509Certificate2 mitIdRootCertiticate = new X509Certificate2(LookupTest.mitIdRootCertificate); IList list = new List(); - + list.Add(oces2RootCertificate); + list.Add(mitIdRootCertiticate); OcspLookup ocspLookup = new OcspLookup(ocspConfig, list); @@ -76,36 +83,199 @@ namespace dk.gov.oiosi.test.unit.security.revocation // } //} - - /* - * Not the OCSP job to check for expired certificate - * [Test] - public void LookupTestExpiredFoces1() + + /* + * Not the OCSP job to check for expired certificate + * [Test] + public void LookupTestExpiredFoces1() + { + try + { + X509Certificate2 certificate = new X509Certificate2(LookupTest.foces1ExpiredCertificate, "Test1234"); + Assert.IsNotNull(certificate, "Test certificate was null."); + + OcspLookup ocspLookup = this.CreateOcesLookup(); + RevocationResponse response = ocspLookup.CheckCertificate(certificate); + Assert.IsFalse(response.IsValid, "Certificate is not valid."); + Assert.IsNull(response.Exception, "The lookup return an exception."); + Assert.AreEqual(RevocationCheckStatus.CertificateRevoked, response.RevocationCheckStatus, "Not all check was performed."); + } + catch (Exception exception) + { + Assert.Fail(exception.ToString()); + } + }*/ + /* + [Test] + public void testMitIdTestCertificate() throws Exception { + KeyStore p12 = KeyStore.getInstance("pkcs12"); + p12.load(new FileInputStream(TestConstants.PATH_MITID_ORG_TEST), "?3ngCR4,gq86".toCharArray()); + Enumeration e = p12.aliases(); + Assert.True("No elements found", e.hasMoreElements()); + string alias = e.nextElement(); + X509Certificate certificate = (X509Certificate)p12.getCertificate(alias); + Assert.AreEqual("Wrong cert. subject found", "C=DK, OID.2.5.4.97=NTRDK-90146280, O=Testorganisation nr. 90146280, SERIALNUMBER=UI:DK-O:G:3c0f8cbc-4abe-4c6b-b40f-7236a2f39c7c, CN=Nemhandel-DEV-OCES-cert-20210422", certificate.getSubjectDN().TOString()); + + System.err.println(certificate.getIssuerX500Principal()); + System.err.println(certificate.getSubjectX500Principal()); + + //assertEquals("Wrong cert. found", "SERIALNUMBER=CVR:30808460-FID:94731315 + CN=TU GENEREL FOCES gyldig (funktionscertifikat), O=NETS DANID A/S // CVR:30808460, C=DK", c.getSubjectDN().toString()); + + RevocationResponse revocationResponse = CreateOcesLookup().revocationResponseOnline(certificate); + assertTrue("Certificate should be OCSP valid...", revocationResponse.isValid()); + + }*/ + /** + * Verify that our self-signed check functionality works. + */ + + [Test] + public void CheckCertificateResources() + { + try { - X509Certificate2 certificate = new X509Certificate2(LookupTest.foces1ExpiredCertificate, "Test1234"); - Assert.IsNotNull(certificate, "Test certificate was null."); + var certificate1 = new X509Certificate2(LookupTest.mitIdFocesOkayCertificate, "?3ngCR4,gq86"); + Assert.IsNotNull(certificate1); + } + catch (CryptographicException ex) + { + Console.WriteLine("Kunne ikke finde MitId test certifikat (MachineKeySet): " + LookupTest.mitIdFocesOkayCertificate); + Assert.Fail(ex.ToString()); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + + try + { + var certificate1 = new X509Certificate2(LookupTest.mitIdFocesOkayCertificate, "?3ngCR4,gq86"); + Assert.IsNotNull(certificate1); + } + catch (CryptographicException ex) + { + Console.WriteLine("Kunne ikke finde MitId test certifikat (Default): " + LookupTest.mitIdFocesOkayCertificate); + Assert.Fail(ex.ToString()); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + + try + { + var certificate2 = new X509Certificate2(LookupTest.foces2OkayCertificate, "Test1234"); + Assert.IsNotNull(certificate2); + } + catch (CryptographicException ex) + { + Console.WriteLine("Kunne ikke finde Foces2 test certifikat: " + LookupTest.foces2OkayCertificate); + Assert.Fail(ex.ToString()); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + + try + { + var oces2RootCertificate = new X509Certificate2(LookupTest.oces2RootCertificate); + Assert.IsNotNull(oces2RootCertificate); + } + catch (CryptographicException ex) + { + Console.WriteLine("Kunne ikke finde Foces2 test certifikat: " + LookupTest.oces2RootCertificate); + Assert.Fail(ex.ToString()); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + + try + { + var mitIdRootCertiticate = new X509Certificate2(LookupTest.mitIdRootCertificate); + Assert.IsNotNull(mitIdRootCertiticate); + } + catch (CryptographicException ex) + { + Console.WriteLine("Kunne ikke finde Foces2 test certifikat: " + LookupTest.mitIdRootCertificate); + Assert.Fail(ex.ToString()); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + + + } + + + [Test] + public void TestMitIdTestCertificateRoot() + { + + var certificate = new X509Certificate2(LookupTest.mitIdFocesOkayCertificate, "?3ngCR4,gq86", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); + OcspLookup ocspLookup = this.CreateOcesLookup(); + + var issuerCertificate = new CertificateUtil().FindIssuerCertificate(certificate); + Assert.AreEqual("C=DK, O=Den Danske Stat, OU=Test - cti, CN=Den Danske Stat OCES udstedende-CA 1", issuerCertificate.SubjectName.Name, "Wrong issuer certificate found"); + + var rootCertificate = new CertificateUtil().FindIssuerCertificate(issuerCertificate); + Assert.AreEqual("C=DK, O=Den Danske Stat, OU=Test - cti, CN=Den Danske Stat OCES rod-CA", rootCertificate.SubjectName.Name, "Wrong root certificate found"); + + try + { + ocspLookup.RevocationResponseOnline(rootCertificate); + Assert.Fail("Exception not thrown - as expected"); + //fail("Exception not thrown - as expected"); + } + catch (CheckCertificateOcspUnexpectedException ex) + { + Assert.AreEqual("E-RSP19442: Certificate not trusted, as the certificate is self-signed", ex.Message, "Wrong error"); + } + + + + } + + [Test] + public void LookupMitIdTestCertificate() + { + try + { + + var certificate = new X509Certificate2(LookupTest.mitIdFocesOkayCertificate, "?3ngCR4,gq86", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); + Assert.AreEqual("C=DK, OID.2.5.4.97=NTRDK-90146280, O=Testorganisation nr. 90146280, SERIALNUMBER=UI:DK-O:G:3c0f8cbc-4abe-4c6b-b40f-7236a2f39c7c, CN=Nemhandel-DEV-OCES-cert-20210422", certificate.Subject, "Wrong cert. subject found"); + + + var ocspLookup = CreateOcesLookup(); + + RevocationResponse revocationResponse = ocspLookup.CheckCertificate(certificate); + Assert.IsTrue(revocationResponse.IsValid, "Certificate should be OCSP valid..."); + + + - OcspLookup ocspLookup = this.CreateOcesLookup(); - RevocationResponse response = ocspLookup.CheckCertificate(certificate); - Assert.IsFalse(response.IsValid, "Certificate is not valid."); - Assert.IsNull(response.Exception, "The lookup return an exception."); - Assert.AreEqual(RevocationCheckStatus.CertificateRevoked, response.RevocationCheckStatus, "Not all check was performed."); } catch (Exception exception) { Assert.Fail(exception.ToString()); } - }*/ - + } + + + [Test] public void LookupTestOkayFoces2() { try { - X509Certificate2 certificate = new X509Certificate2(LookupTest.foces2OkayCertificate, "Test1234"); + + X509Certificate2 certificate = new X509Certificate2(LookupTest.foces2OkayCertificate, "Test1234", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); Assert.IsNotNull(certificate, "Test certificate was null."); OcspLookup ocspLookup = this.CreateOcesLookup(); @@ -113,12 +283,14 @@ namespace dk.gov.oiosi.test.unit.security.revocation Assert.IsTrue(response.IsValid, "Certificate is not valid."); Assert.IsNull(response.Exception, "The lookup return an exception."); Assert.AreEqual(RevocationCheckStatus.AllChecksPassed, response.RevocationCheckStatus, "Not all check was performed."); + + } catch (Exception exception) { Assert.Fail(exception.ToString()); } - } + } [Test] [Ignore("Certificate expired - get a fresh one!!!")] @@ -165,22 +337,22 @@ namespace dk.gov.oiosi.test.unit.security.revocation /* */ - /* [Test] - public void LookupTestExpiredFoces2() - { - try - { - OcspLookup ocspLookup = this.CreateOcesLookup(); - X509Certificate2 certificate = new X509Certificate2(this.foces2ExpiredCertificate, "Test1234"); - RevocationResponse response = ocspLookup.CheckCertificate(certificate); - Assert.IsFalse(response.IsValid, "Certificate is not valid."); - Assert.IsNull(response.Exception, "The lookup return an exception."); - Assert.AreEqual(RevocationCheckStatus.CertificateRevoked, response.RevocationCheckStatus, "Not all check was performed."); - } - catch (Exception exception) - { - Assert.Fail(exception.ToString()); - } - }*/ + /* [Test] + public void LookupTestExpiredFoces2() + { + try + { + OcspLookup ocspLookup = this.CreateOcesLookup(); + X509Certificate2 certificate = new X509Certificate2(this.foces2ExpiredCertificate, "Test1234"); + RevocationResponse response = ocspLookup.CheckCertificate(certificate); + Assert.IsFalse(response.IsValid, "Certificate is not valid."); + Assert.IsNull(response.Exception, "The lookup return an exception."); + Assert.AreEqual(RevocationCheckStatus.CertificateRevoked, response.RevocationCheckStatus, "Not all check was performed."); + } + catch (Exception exception) + { + Assert.Fail(exception.ToString()); + } + }*/ } } \ No newline at end of file