diff --git a/FitConnect/Encryption/CertificateHelper.cs b/FitConnect/Encryption/CertificateHelper.cs index 1a83aa0e893bd961c03cd72bab4def91021d7dd9..abf25d6cc3de397a75e425ef9c80cc7133810a1f 100644 --- a/FitConnect/Encryption/CertificateHelper.cs +++ b/FitConnect/Encryption/CertificateHelper.cs @@ -1,6 +1,9 @@ using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.Unicode; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; +using Microsoft.Win32.SafeHandles; namespace FitConnect.Encryption; @@ -18,30 +21,48 @@ public class CertificateHelper { var certificates = key.X5c.Select(s => new X509Certificate2(Convert.FromBase64String(s))) .ToList(); + var root = new X509Certificate2("./certificates/root.pem"); + + _logger?.LogTrace("Found {Count} certificate(s)", certificates.Count); var valid = certificates.Aggregate(true, (result, cert) => result - && ValidateCertificate(cert, out var _, logLevel) + && ValidateCertificate(cert, out _, + root, + logLevel) && cert.Verify() ); return valid; } - internal bool ValidateCertificate(X509Certificate2 certificateX509, - out X509ChainStatus[] chainStatus, LogLevel logLevel = LogLevel.Warning) { + internal bool ValidateCertificate(X509Certificate2 certificate, + out X509ChainStatus[] chainStatus, + X509Certificate2? rootCertificate = null, + LogLevel logLevel = LogLevel.Warning) { var certificateChain = new X509Chain(); + + + if (rootCertificate != null) { + certificateChain.ChainPolicy.CustomTrustStore.Add(rootCertificate); + certificateChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + } + certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; certificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; certificateChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30); - certificateChain.Build(certificateX509); + + var result = certificateChain.Build(certificate); chainStatus = certificateChain.ChainStatus; - _logger?.Log(logLevel, "Certificate status: {ObjStatusInformation}", - certificateChain.ChainStatus.Aggregate("", - (r, s) => r + "\n\t - " + s.Status + ": " + s.StatusInformation)); - return certificateChain.ChainStatus.Length == 0; + var statusAggregation = certificateChain.ChainStatus.Aggregate("", + (r, s) => r + "\n\t - " + s.Status + ": " + s.StatusInformation); + + if (!string.IsNullOrWhiteSpace(statusAggregation)) + _logger?.Log(logLevel, "Certificate status: {ObjStatusInformation}", + statusAggregation); + return result; } } diff --git a/IntegrationTests/CertificateValidation.cs b/IntegrationTests/CertificateValidation.cs index c9e4d83e339c77b8b19517ed66fbffd3eeb2d4cb..38f3268242d80cdb50eca1b5148416092e0f4997 100644 --- a/IntegrationTests/CertificateValidation.cs +++ b/IntegrationTests/CertificateValidation.cs @@ -64,7 +64,7 @@ public class CertificateValidation { .WithServiceType("", _settings.LeikaKey) .WithAttachments(new Attachment("Test.pdf", "Simple Test PDF")) .Submit(); - }).InnerExceptions.Any(e=>e.GetType() == typeof(SecurityException)); + }).InnerExceptions.Any(e => e.GetType() == typeof(SecurityException)); } [Test] @@ -101,10 +101,45 @@ public class CertificateValidation { [Test] public void CheckPemFiles() { var files = System.IO.Directory.GetFiles("./certificates"); - foreach (var fileName in files) { + var success = 0; + var failed = 0; + + foreach (var fileName in files.Where(f => !f.EndsWith("root.pem"))) { _logger.LogInformation("Checking file: {FileName}", fileName); - var certificate = X509Certificate2.CreateFromPem(File.ReadAllText(fileName)); - _certificateHelper.ValidateCertificate(certificate, out var states); + + + if (fileName.EndsWith(".pem")) { + var certificate = X509Certificate2.CreateFromPem(File.ReadAllText(fileName)); + var valid = _certificateHelper.ValidateCertificate(certificate, out var states, + null); + if (valid) { + success++; + } + else { + failed++; + } + } + + if (fileName.EndsWith(".json")) { + var shouldFail = !fileName.Contains("/valid"); + + var jwk = new JsonWebKey(File.ReadAllText(fileName)); + var valid = _certificateHelper.ValidateCertificate(jwk, + shouldFail ? LogLevel.Trace : LogLevel.Critical); + + if (shouldFail) + valid = !valid; + + if (valid) { + success++; + } + else { + failed++; + } + } } + + _logger.LogInformation("Success: {Success}, Failed: {Failed}", success, failed); + failed.Should().Be(0); } } diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj index a37d2d39fe07b8377397a922e66c547acc8cb9a1..d666140b81c9e71e541c2ee9535b7d962ba541cf 100644 --- a/IntegrationTests/IntegrationTests.csproj +++ b/IntegrationTests/IntegrationTests.csproj @@ -36,6 +36,24 @@ <None Update="certificates\www-amazon-de-zertifikatskette.pem"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> + <None Update="certificates\invalidEncJwkWithLessThan3Certificates.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\validEncJW_KeyUse.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\ValidEncJWK.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\validSigJWK.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\revokedEncJWK.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\root.pem"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> </Project>