Skip to content

Commit

Permalink
Changes to Certificate Validation (#714)
Browse files Browse the repository at this point in the history
* Changes to Certificate Validation

- X509Chain is moved to be initated when certificate is being validated
  from the constructor of the class
- X509ChainMock removed as X509Chain no longer can be mocked
- Removed tests of X509Chain status as it no longer can be tested
  without a mock

* Added link to migrate to rest in readme

* Updated documentations and fixed tests
  • Loading branch information
ArneHB authored Nov 27, 2024
1 parent 469c7b6 commit 522c280
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 185 deletions.
2 changes: 1 addition & 1 deletion Directory.build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>10.0</LangVersion>
<Version>5.2.0</Version>
<Version>5.2.1</Version>
</PropertyGroup>
<PropertyGroup>
<Authors>Norsk Helsenett SF</Authors>
Expand Down
2 changes: 1 addition & 1 deletion Documentation/MigrateFromSoapToRest.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Migrere fra 5.0 til 5.2
## Migrere fra SOAP til REST i 5.2 og senere versjoner

Hovedendringer for 5.2 er nytt endepunkt for CPPA tjenesten. Det gamle endepunktet vil fortsatt fungere, men
støtten for SOAP-endepunktet vil fjernes i en fremtidig versjon. Derfor anbefaler vi at alle oppgraderer
Expand Down
6 changes: 3 additions & 3 deletions Documentation/Registre.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
## Registre
For å kunne kommunisere riktig med en mottpart, så er vi avhengig av å vite en del om protokoller, sertifikater, og kønavn. All denne informasjonen ligger i adresseregisteret og CPA-registeret.

Meldingsutvekslingen er ekstremt avhengig av adresseregisteret og CPA-registeret.
Meldingsutvekslingen er avhengig av adresseregisteret og CPA-registeret.

### Registerintegrasjon

Før man kan sette opp infrastrukturen for meldinger, så må man ha registerintegrasjonen på plass og HelseId klient.
Denne koden bruker klasser fra andre Microsoft.Extensions.* pakkker.
Før man kan sette opp infrastrukturen for meldinger, så må man ha registerintegrasjonen på plass og HelseId.
Denne koden bruker klasser fra andre Microsoft.Extensions.* pakkker. Se også Helsenorge.Messaging.Client prosjektet for eksempler på hvordan det kan settes opp.

```cs
var loggerFactory = new LoggerFactory();
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Dette biblioteket støtter oppunder basisbehovet for en kommunikasjonsløsning v
12. [Biztalk](Documentation/Biztalk.md "Biztalk")
13. [Migrere fra 3.0 til 4.0](Documentation/MigrateFrom3To4.md)
14. [Migrere fra 4.0 til 5.0](Documentation/MigrateFrom4To5.md)
14. [Migrere fra SOAP til REST for 5.2 og senere versjoner](Documentation/MigrateFromSoapToRest.md)


## Contributing
Expand Down
37 changes: 13 additions & 24 deletions src/Helsenorge.Registries/CertificateValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System;
using System.Security.Cryptography.X509Certificates;
using Helsenorge.Registries.Abstractions;
using Helsenorge.Registries.Utilities;
using Microsoft.Extensions.Logging;

namespace Helsenorge.Registries
Expand All @@ -21,7 +20,6 @@ public class CertificateValidator : ICertificateValidator
{
private readonly ILogger _logger;
private readonly bool _useOnlineRevocationCheck;
private readonly IX509Chain _chain;

/// <summary>
/// CertificateValidator constructor
Expand All @@ -32,20 +30,6 @@ public CertificateValidator(ILogger logger, bool useOnlineRevocationCheck = true
{
_logger = logger;
_useOnlineRevocationCheck = useOnlineRevocationCheck;
_chain = new X509ChainWrapper();
}

/// <summary>
/// CertificateValidator constructor
/// </summary>
/// <param name="chain">You can set your own X509Chain.</param>
/// <param name="logger">Default logger</param>
/// <param name="useOnlineRevocationCheck">Should online certificate revocation list be used. Optional, default true.</param>
internal CertificateValidator(IX509Chain chain, ILogger logger, bool useOnlineRevocationCheck = true)
{
_useOnlineRevocationCheck = useOnlineRevocationCheck;
_chain = chain;
_logger = logger;
}

/// <summary>
Expand Down Expand Up @@ -73,17 +57,22 @@ public CertificateErrors Validate(X509Certificate2 certificate, X509KeyUsageFlag
if (!certificate.HasKeyUsage(usage))
result |= CertificateErrors.Usage;

var chain = new X509Chain
{
ChainPolicy =
{
RevocationMode = _useOnlineRevocationCheck ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
RevocationFlag = X509RevocationFlag.EntireChain,
UrlRetrievalTimeout = TimeSpan.FromSeconds(30),
VerificationTime = DateTime.Now,
}
};

_chain.ChainPolicy.RevocationMode = _useOnlineRevocationCheck ? X509RevocationMode.Online : X509RevocationMode.NoCheck;
_chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
_chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(30);
_chain.ChainPolicy.VerificationTime = DateTime.Now;

using (_chain)
using (chain)
{
if (_chain.Build(certificate)) return result;
if (chain.Build(certificate)) return result;

foreach (var status in _chain.ChainStatus)
foreach (var status in chain.ChainStatus)
{
_logger.LogInformation("Certificate chain validation. " +
$"Status Information: {status.StatusInformation} Status Flag: {status.Status}");
Expand Down
12 changes: 0 additions & 12 deletions src/Helsenorge.Registries/Utilities/IX509Chain.cs

This file was deleted.

28 changes: 0 additions & 28 deletions src/Helsenorge.Registries/Utilities/X509ChainWrapper.cs

This file was deleted.

105 changes: 23 additions & 82 deletions test/Helsenorge.Registries.Tests/CertificateValidatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
* available at https://raw.githubusercontent.com/helsenorge/Helsenorge.Messaging/master/LICENSE
*/

using System.Security.Cryptography;
using System;
using System.Security.Cryptography.X509Certificates;
using Helsenorge.Registries.Abstractions;
using Helsenorge.Registries.Tests.Mocks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand All @@ -34,41 +35,49 @@ public void Setup()
[TestMethod]
public void CertificateValidation_ArgumentNullException()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(null, X509KeyUsageFlags.NonRepudiation);
Assert.AreEqual(CertificateErrors.Missing, error);
}
[TestMethod]
public void CertificateValidation_None()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(TestCertificates.CounterpartyPublicSignature,
X509KeyUsageFlags.NonRepudiation);
Assert.AreEqual(CertificateErrors.None, error);
// Added RevokedUnknown as build server add this error
Assert.IsTrue(error == CertificateErrors.None
|| error == CertificateErrors.RevokedUnknown);
}
[TestMethod]
public void CertificateValidation_StartDate()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(TestCertificates.CounterpartyPublicSignatureInvalidStart,
X509KeyUsageFlags.NonRepudiation);
Assert.AreEqual(CertificateErrors.StartDate, error);
// Added RevokedUnknown as build server add this error
Assert.IsTrue(error == CertificateErrors.StartDate
|| error == (CertificateErrors.StartDate | CertificateErrors.RevokedUnknown));
}
[TestMethod]
public void CertificateValidation_EndDate()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(TestCertificates.CounterpartyPublicSignatureInvalidEnd,
X509KeyUsageFlags.NonRepudiation);
Assert.AreEqual(CertificateErrors.EndDate, error);
// Added RevokedUnknown as build server add this error
Assert.IsTrue(error == CertificateErrors.EndDate
|| error == (CertificateErrors.EndDate | CertificateErrors.RevokedUnknown));
}
[TestMethod]
public void CertificateValidation_Usage()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(TestCertificates.CounterpartyPublicSignature,
X509KeyUsageFlags.KeyEncipherment);
Assert.AreEqual(CertificateErrors.Usage, error);
// Added RevokedUnknown as build server add this error
Assert.IsTrue(error == CertificateErrors.Usage
|| error == (CertificateErrors.Usage | CertificateErrors.RevokedUnknown));
}
[TestMethod]
public void X509Certificate2Extensions_KeyUsage()
Expand All @@ -80,80 +89,12 @@ public void X509Certificate2Extensions_KeyUsage()
[TestMethod]
public void CertificateValidation_Multiple()
{
var validator = new CertificateValidator(new MockX509Chain(), _logger);
var validator = new CertificateValidator(_logger);
var error = validator.Validate(TestCertificates.CounterpartyPublicSignatureInvalidStart,
X509KeyUsageFlags.KeyEncipherment);
Assert.AreEqual(CertificateErrors.StartDate | CertificateErrors.Usage, error);
}

[TestMethod]
public void CertificateValidation_RevokeOffline()
{
var usage = X509KeyUsageFlags.NonRepudiation;
var testCertificate = TestCertificates.CounterpartyPublicSignature;

var mockChain = new MockX509Chain();
mockChain.SetChainStatus(new[]
{
new X509ChainStatus
{
Status = X509ChainStatusFlags.OfflineRevocation,
StatusInformation = "Offline revocation"
}
});
var validator = new CertificateValidator(mockChain, _logger);
var error = validator.Validate(testCertificate, usage);
Assert.AreEqual(CertificateErrors.RevokedOffline, error);
}

[TestMethod]
public void CertificateValidation_RevokeMultiple()
{
var usage = X509KeyUsageFlags.NonRepudiation;
var testCertificate = TestCertificates.CounterpartyPublicSignature;

var mockChain = new MockX509Chain();
mockChain.SetChainStatus(new[]
{
new X509ChainStatus
{
Status = X509ChainStatusFlags.OfflineRevocation,
StatusInformation = "Offline revocation"
},
new X509ChainStatus
{
Status = X509ChainStatusFlags.Revoked,
StatusInformation = "Revoked"
}
});
var validator = new CertificateValidator(mockChain, _logger);
var error = validator.Validate(testCertificate, usage);
Assert.AreEqual(CertificateErrors.RevokedOffline | CertificateErrors.Revoked, error);
}

[TestMethod]
public void CertificateValidation_ChainStatusOther()
{
var usage = X509KeyUsageFlags.NonRepudiation;
var testCertificate = TestCertificates.CounterpartyPublicSignature;

var mockChain = new MockX509Chain();
mockChain.SetChainStatus(new[]
{
new X509ChainStatus
{
Status = X509ChainStatusFlags.HasWeakSignature,
StatusInformation = "Has weak signature"
},
new X509ChainStatus
{
Status = X509ChainStatusFlags.InvalidExtension,
StatusInformation = "Invalid extension"
}
});
var validator = new CertificateValidator(mockChain, _logger);
var error = validator.Validate(testCertificate, usage);
Assert.AreEqual(CertificateErrors.None, error);
// Added RevokedUnknown as build server add this error
Assert.IsTrue(error == (CertificateErrors.StartDate | CertificateErrors.Usage)
|| error == (CertificateErrors.StartDate | CertificateErrors.Usage | CertificateErrors.RevokedUnknown));
}
}
}
34 changes: 0 additions & 34 deletions test/Helsenorge.Registries.Tests/Mocks/MockX509Chain.cs

This file was deleted.

0 comments on commit 522c280

Please sign in to comment.