From f92af338e7b35c49ef9566e47949b70d90ed4745 Mon Sep 17 00:00:00 2001 From: Klaus Fischer <klaus.fischer@eloware.com> Date: Thu, 15 Sep 2022 09:27:02 +0200 Subject: [PATCH] Working --- FitConnect/Encryption/CertificateHelper.cs | 68 +++++++++++++++++++--- FitConnect/FitConnectClient.cs | 8 --- FitConnect/Services/SubmissionService.cs | 17 +++++- IntegrationTests/CertificateValidation.cs | 38 ++++++------ IntegrationTests/IntegrationTests.csproj | 36 ++++++++++++ IntegrationTests/ProxyTest.cs | 8 ++- 6 files changed, 138 insertions(+), 37 deletions(-) diff --git a/FitConnect/Encryption/CertificateHelper.cs b/FitConnect/Encryption/CertificateHelper.cs index f3697349..92a6abee 100644 --- a/FitConnect/Encryption/CertificateHelper.cs +++ b/FitConnect/Encryption/CertificateHelper.cs @@ -1,3 +1,4 @@ +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Unicode; @@ -29,19 +30,16 @@ public class CertificateHelper { _logger?.LogDebug("Issuers: {Issuer}", certificate.Issuer); if (rootCertificate != null) { - certificateChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; certificateChain.ChainPolicy.CustomTrustStore.AddRange(rootCertificate); - certificateChain.ChainPolicy.ExtraStore.AddRange(rootCertificate); - certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; - certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; - certificateChain.ChainPolicy.DisableCertificateDownloads = false; + certificateChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Offline; + certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; _logger?.LogDebug("Using custom root certificate"); } else { certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; certificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; - certificateChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30); } @@ -53,9 +51,11 @@ public class CertificateHelper { var statusAggregation = certificateChain.ChainStatus.Aggregate("", (r, s) => r + "\n\t - " + s.Status + ": " + s.StatusInformation); - if (certificateChain.ChainStatus.Length > 0) + if (certificateChain.ChainStatus.Length > 0) { _logger?.Log(logLevel, "Certificate status: {ObjStatusInformation}", statusAggregation); + // _logger?.Log(logLevel, "Chain status: {ChainStatus}", certificateChain.ToDisplayString()); + } return result; } @@ -93,4 +93,58 @@ public static class X509Certificate2Extensions { File.WriteAllText(fileName + ".pem", content); File.WriteAllText(fileName + ".json", JsonConvert.SerializeObject(certificate)); } + + public static string ToDisplayString(this X509Chain chain) { + var b = new StringBuilder(); + + b.AppendLine($"[{nameof(chain.ChainPolicy.RevocationFlag)}]"); + b.AppendLine($" {chain.ChainPolicy.RevocationFlag}"); + b.AppendLine(); + b.AppendLine($"[{nameof(chain.ChainPolicy.RevocationMode)}]"); + b.AppendLine($" {chain.ChainPolicy.RevocationMode}"); + b.AppendLine(); + b.AppendLine($"[{nameof(chain.ChainPolicy.VerificationFlags)}]"); + b.AppendLine($" {chain.ChainPolicy.VerificationFlags}"); + b.AppendLine(); + b.AppendLine($"[{nameof(chain.ChainPolicy.VerificationTime)}]"); + b.AppendLine($" {chain.ChainPolicy.VerificationTime}"); + b.AppendLine(); + b.AppendLine($"[Application Policy]"); + foreach (var policy in chain.ChainPolicy.ApplicationPolicy) { + b.AppendLine($" {policy.ToDisplayString()}"); + } + + b.AppendLine(); + b.AppendLine($"[Certificate Policy]"); + foreach (var policy in chain.ChainPolicy.CertificatePolicy) { + b.AppendLine($" {policy.ToDisplayString()}"); + } + + b.AppendLine(); + b.AppendLine($"[Elements]"); + foreach (var (element, index) in chain.ChainElements.Cast<X509ChainElement>() + .Select((element, index) => (element, index))) { + b.AppendLine(); + b.AppendLine($"[Element {index + 1}]"); + b.AppendLine(); + b.Append(element.Certificate.ToString()); + b.AppendLine(); + b.AppendLine($"[Status]"); + foreach (var status in element.ChainElementStatus) { + b.AppendLine($" {status.ToDisplayString()}"); + } + } + + return b.ToString(); + } + + public static string ToDisplayString(this Oid oid) { + return oid.FriendlyName == oid.Value + ? $"{oid.Value}" + : $"{oid.FriendlyName}: {oid.Value}"; + } + + public static string ToDisplayString(this X509ChainStatus status) { + return $"{status.Status}: {status.StatusInformation}"; + } } diff --git a/FitConnect/FitConnectClient.cs b/FitConnect/FitConnectClient.cs index fa1c531f..6e348149 100644 --- a/FitConnect/FitConnectClient.cs +++ b/FitConnect/FitConnectClient.cs @@ -106,14 +106,6 @@ public abstract class FitConnectClient { var events = SubmissionService.GetStatusForSubmissionAsync(caseId, destinationId, skipTest) .Result?.Select(e => new SecurityEventToken(e!)).ToList() ?? new List<SecurityEventToken>(); - - // Check OCSP Signature of SET Event - #warning Sender has no way to verify the OCSP signature - if (_publicKeySignatureVerification != null) { - var setsSignatureValid = events.Aggregate(true, - (run, e) => run &= FitEncryption.VerifyJwt(e.TokenString, - new JsonWebKey(_publicKeySignatureVerification), Logger)); - } return events; } diff --git a/FitConnect/Services/SubmissionService.cs b/FitConnect/Services/SubmissionService.cs index b937358b..f29a496a 100644 --- a/FitConnect/Services/SubmissionService.cs +++ b/FitConnect/Services/SubmissionService.cs @@ -254,8 +254,21 @@ internal class SubmissionService : RestCallService, ISubmissionService { : keySet.Keys.Append(_signatureValidationKey)).ToList(); - return (await GetKeyIdsFromEvent(events, destinationId, keys.Select(k => k.Kid).ToList())) - .Union(keys); + var result = + (await GetKeyIdsFromEvent(events, destinationId, keys.Select(k => k.Kid).ToList())) + .Union(keys).ToList(); + + var valid = result.Aggregate(true, + (a, key) => (new CertificateHelper(_logger).ValidateCertificate(key))); + if (!valid) { + _logger?.LogError("(SET Events verification) Invalid certificate"); + throw new ArgumentException("(SET Events verification) Invalid certificate"); + } + else { + _logger?.LogInformation("(SET Events verification) Certificate is valid"); + } + + return result; } private async Task<IEnumerable<JsonWebKey>> GetKeyIdsFromEvent(EventLogDto events, diff --git a/IntegrationTests/CertificateValidation.cs b/IntegrationTests/CertificateValidation.cs index 7a26e50a..c0ca8ac6 100644 --- a/IntegrationTests/CertificateValidation.cs +++ b/IntegrationTests/CertificateValidation.cs @@ -130,37 +130,29 @@ public class CertificateValidation { .Should().BeTrue(); } + [Ignore("Not the scope of this branch - reactivate later")] [Test] public void CheckPemFiles() { - var files = System.IO.Directory.GetFiles("./certificates"); + var files = Directory.GetFiles("./certificates"); var success = 0; var failed = 0; var failedCerts = new List<string>(); - foreach (var fileName in files.Where(f => !f.EndsWith("root.pem"))) { + foreach (var fileName in files) { _logger.LogInformation("Checking file: {FileName}", fileName); + var certificateContents = Directory.GetFiles("./certificates/roots"); + var rootCertificates = certificateContents + .Select(file => new X509Certificate2(file)).ToArray(); + certificateContents.Length.Should().Be(rootCertificates.Length); - 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++; - failedCerts.Add(fileName); - } - } if (fileName.EndsWith(".json")) { var shouldFail = !fileName.Contains("/valid"); var jwk = new JsonWebKey(File.ReadAllText(fileName)); var valid = _certificateHelper.ValidateCertificate(jwk, - shouldFail ? LogLevel.Warning : LogLevel.Critical, - Directory.GetFiles("./certificates/roots") - .Select(file => new X509Certificate2(file)).ToArray()); + shouldFail ? LogLevel.Warning : LogLevel.Critical, rootCertificates + ); if (shouldFail) valid = !valid; @@ -173,6 +165,18 @@ public class CertificateValidation { failedCerts.Add(fileName); } } + else { + var certificate = new X509Certificate2(fileName); + var valid = _certificateHelper.ValidateCertificate(certificate, out var states, + null); + if (valid) { + success++; + } + else { + failed++; + failedCerts.Add(fileName); + } + } } _logger.LogWarning("Failed certificates: {Certs}", diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj index 87826f23..8dd3721c 100644 --- a/IntegrationTests/IntegrationTests.csproj +++ b/IntegrationTests/IntegrationTests.csproj @@ -144,6 +144,42 @@ <None Update="certificates\valid_productionKey.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> + <None Update="certificates\roots\ca.1212.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1230.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1249.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1269.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1286.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1305.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1322.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1340.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\roots\ca.1357.der"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\validEncJWK_KeyUse.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\ValidEncJWK.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="certificates\validSigJWK.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> </Project> diff --git a/IntegrationTests/ProxyTest.cs b/IntegrationTests/ProxyTest.cs index 275d7cd9..7d7fee7f 100644 --- a/IntegrationTests/ProxyTest.cs +++ b/IntegrationTests/ProxyTest.cs @@ -13,6 +13,8 @@ using NUnit.Framework; namespace IntegrationTests; +// Change to https://hub.docker.com/r/mitmproxy/mitmproxy/ + [Ignore("Not testable in docker container, take to long to run every time")] [TestFixture] public class ProxyTest { @@ -25,8 +27,8 @@ public class ProxyTest { File.WriteAllText("proxy/access.log", ""); _container = new TestcontainersBuilder<TestcontainersContainer>() - .WithImage("ubuntu/squid") - .WithPortBinding("3128", "3128") + .WithImage("mitmproxy/mitmproxy") + .WithPortBinding("3128", "8081") .WithBindMount(path, @"/var/log/squid") .Build(); _container.StartAsync().Wait(); @@ -39,7 +41,7 @@ public class ProxyTest { new FitConnect.Sender(FitConnectEnvironment.Testing, _id, _secret) - .WithProxy("localhost", 3128); + .WithProxy("localhost", 3128, null, null); #pragma warning disable SYSLIB0014 _webClient = new WebClient { -- GitLab