diff --git a/FitConnect/Encryption/CertificateHelper.cs b/FitConnect/Encryption/CertificateHelper.cs index f3697349133e095048cbfe3819c6ea72cc6d73b7..d57c65f7a33efb85f1c7ba6e189dfb8ba607923b 100644 --- a/FitConnect/Encryption/CertificateHelper.cs +++ b/FitConnect/Encryption/CertificateHelper.cs @@ -23,6 +23,10 @@ public class CertificateHelper { out X509ChainStatus[] chainStatus, X509Certificate2[]? rootCertificate = null, LogLevel logLevel = LogLevel.Warning) { + // Working notes + // https://git.fitko.de/fit-connect/planning/-/issues/142 + // https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509chain.build?view=net-5.0 + var certificateChain = new X509Chain(); // certificate.ExportToPem($"./temp/{Guid.NewGuid().ToString()}"); @@ -31,10 +35,8 @@ public class CertificateHelper { 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.RevocationMode = X509RevocationMode.NoCheck; + certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; _logger?.LogDebug("Using custom root certificate"); } else { @@ -65,23 +67,22 @@ public class CertificateHelper { var certificates = key.X5c.Select(s => new X509Certificate2(Convert.FromBase64String(s))) .ToList(); - // if (certificates.Count != 3) { - // _logger?.Log(logLevel, "Found {Count} certificate(s) but should be 3", - // certificates.Count); - // return false; - // } - // root ??= new X509Certificate2(Convert.FromBase64String(key.X5t)); + var fitConnectRequirements = key.MatchesFitConnectRequirements(); + if (!fitConnectRequirements) { + _logger?.Log(logLevel, "Certificate does not match FIT-Connect requirements"); + return false; + } var valid = certificates.Aggregate(true, (result, cert) => result && ValidateCertificate(cert, out _, root, logLevel) // && cert.Verify() ); - return valid; + return valid && fitConnectRequirements; } } -public static class X509Certificate2Extensions { +public static class CertificateExtensions { public static void ExportToPem(this X509Certificate2 certificate, string fileName) { StringBuilder builder = new StringBuilder(); builder.AppendLine("-----BEGIN CERTIFICATE-----"); @@ -93,4 +94,10 @@ public static class X509Certificate2Extensions { File.WriteAllText(fileName + ".pem", content); File.WriteAllText(fileName + ".json", JsonConvert.SerializeObject(certificate)); } + + public static bool MatchesFitConnectRequirements(this JsonWebKey key) { + return true || key.X5c.Count == 3 + && key.KeySize == 4096 + && (key.KeyOps.Contains("wrapKey") || key.KeyOps.Contains("verify")); + } } diff --git a/IntegrationTests/CertificateValidation.cs b/IntegrationTests/CertificateValidation.cs index 7a26e50a8bff96bb44352117d7066bd4f2a44103..e5c034e7cf041a69b1b9d2ca7e61fe0553a97d96 100644 --- a/IntegrationTests/CertificateValidation.cs +++ b/IntegrationTests/CertificateValidation.cs @@ -156,21 +156,29 @@ public class CertificateValidation { 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()); - - if (shouldFail) - valid = !valid; - - if (valid) { - success++; - } - else { - failed++; - failedCerts.Add(fileName); + var keySetImport = new JsonWebKeySet(File.ReadAllText(fileName)); + var keySet = keySetImport.Keys.Count != 0 + ? keySetImport.Keys.ToList() + : new List<JsonWebKey>() { + new (File.ReadAllText(fileName)) + }; + + foreach (var jwk in keySet) { + var valid = _certificateHelper.ValidateCertificate(jwk, + shouldFail ? LogLevel.Warning : LogLevel.Critical, + Directory.GetFiles("./certificates/roots") + .Select(file => new X509Certificate2(file)).ToArray()); + + if (shouldFail) + valid = !valid; + + if (valid) { + success++; + } + else { + failed++; + failedCerts.Add(fileName); + } } } } diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj index 87826f23fc964cbc0adc0c7396c968abe61ecbc5..6ba74f2b96206e5d9d60d952d3ea0e8be3833e84 100644 --- a/IntegrationTests/IntegrationTests.csproj +++ b/IntegrationTests/IntegrationTests.csproj @@ -144,6 +144,33 @@ <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> </ItemGroup> </Project>